Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

@@ -0,0 +1,119 @@
<?php
/**
* @brief Community Enhancement: Facebook Pixel
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @since 16 May 2017
*/
namespace IPS\core\extensions\core\CommunityEnhancements;
/* 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;
}
/**
* Community Enhancement: Facebook Pixel
*/
class _FacebookPixel
{
/**
* @brief IPS-provided enhancement?
*/
public $ips = FALSE;
/**
* @brief Enhancement is enabled?
*/
public $enabled = FALSE;
/**
* @brief Enhancement has configuration options?
*/
public $hasOptions = TRUE;
/**
* @brief Icon data
*/
public $icon = "facebook.png";
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$this->enabled = ( \IPS\Settings::i()->fb_pixel_enabled and \IPS\Settings::i()->fb_pixel_id );
}
/**
* Edit
*
* @return void
*/
public function edit()
{
$validation = function( $val ) {
if ( $val and !\IPS\Request::i()->fb_pixel_id )
{
throw new \DomainException('fb_pixel_id_req');
}
};
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Text( 'fb_pixel_id', \IPS\Settings::i()->fb_pixel_id ? \IPS\Settings::i()->fb_pixel_id : '', FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'fb_pixel_enabled', \IPS\Settings::i()->fb_pixel_enabled, FALSE, array(), $validation ) );
$form->add( new \IPS\Helpers\Form\Number( 'fb_pixel_delay', \IPS\Settings::i()->fb_pixel_delay, FALSE, array(), $validation, NULL, \IPS\Member::loggedIn()->language()->addToStack('fb_pixel_delay_seconds') ) );
if ( $form->values() )
{
$form->saveAsSettings();
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_FacebookPixel' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
\IPS\Output::i()->sidebar['actions'] = array(
'help' => array(
'title' => 'learn_more',
'icon' => 'question-circle',
'link' => \IPS\Http\Url::ips( 'docs/facebookpixel' ),
'target' => '_blank'
),
);
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'enhancements__core_FacebookPixel', $form );
}
/**
* Enable/Disable
*
* @param $enabled bool Enable/Disable
* @return void
*/
public function toggle( $enabled )
{
if ( $enabled )
{
if ( \IPS\Settings::i()->fb_pixel_id )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'fb_pixel_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
}
else
{
throw new \DomainException;
}
}
else
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'fb_pixel_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
}
}
}
@@ -79,7 +79,6 @@ class _GeoIP
*/
public function toggle( $enabled )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'ipsgeoip' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'ipsgeoip' => 0 ) );
}
}
@@ -112,9 +112,15 @@ class _GoogleMaps
} ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'googlemaps', \IPS\Settings::i()->googlemaps, FALSE, array(), $validation ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'googleplacesautocomplete', \IPS\Settings::i()->googleplacesautocomplete, FALSE, array(), $validation ) );
if ( $form->values() )
if ( $values = $form->values() )
{
$form->saveAsSettings();
if( $values['googlemaps'] > 0 )
{
$values['mapbox'] = 0;
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_GoogleMaps' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
@@ -139,22 +145,21 @@ class _GoogleMaps
*/
public function toggle( $enabled )
{
if ( $enabled )
/* If we're disabling, just disable */
if( !$enabled )
{
if ( \IPS\Settings::i()->google_maps_api_key )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( '( conf_key=? OR conf_key=? )', 'googlemaps', 'googleplacesautocomplete' ) );
unset( \IPS\Data\Store::i()->settings );
}
else
{
throw new \DomainException;
}
\IPS\Settings::i()->changeValues( array( 'googlemaps' => 0, 'googleplacesautocomplete' => 0 ) );
}
/* Otherwise if we already have an API key, just toggle on */
if( $enabled && \IPS\Settings::i()->google_maps_api_key )
{
\IPS\Settings::i()->changeValues( array( 'googlemaps' => 1, 'googleplacesautocomplete' => 1, 'mapbox' => 0 ) );
}
else
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( '( conf_key=? OR conf_key=? )', 'googlemaps', 'googleplacesautocomplete' ) );
unset( \IPS\Data\Store::i()->settings );
/* Otherwise we need to let them enter an API key before we can enable. Throwing an exception causes you to be redirected to the settings page. */
throw new \DomainException;
}
}
}
@@ -0,0 +1,147 @@
<?php
/**
* @brief Community Enhancement: Mapbox
* @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 1 Nov 2017
*/
namespace IPS\core\extensions\core\CommunityEnhancements;
/* 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;
}
/**
* Community Enhancement: Mapbox
*/
class _Mapbox
{
/**
* @brief IPS-provided enhancement?
*/
public $ips = FALSE;
/**
* @brief Enhancement is enabled?
*/
public $enabled = FALSE;
/**
* @brief Enhancement has configuration options?
*/
public $hasOptions = TRUE;
/**
* @brief Icon data
*/
public $icon = "mapbox.png";
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$this->enabled = ( \IPS\Settings::i()->mapbox_api_key and ( \IPS\Settings::i()->mapbox ) );
}
/**
* Edit
*
* @return void
*/
public function edit()
{
$validation = function( $val ) {
if ( $val and !\IPS\Request::i()->mapbox_api_key )
{
throw new \DomainException('mapbox_api_key_req');
}
};
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Text( 'mapbox_api_key', \IPS\Settings::i()->mapbox_api_key, FALSE, array(), function( $val ) {
if ( $val )
{
/* Check API */
try
{
$location = '-73.961452,40.714224';
$response = \IPS\Http\Url::external( "https://api.mapbox.com/geocoding/v5/mapbox.places/{$location}.json" )->setQueryString( array(
'access_token' => $val,
) )->request()->get()->decodeJson();
}
catch ( \Exception $e )
{
throw new \DomainException('mapbox_api_error');
}
if ( isset( $response['message'] ) )
{
throw new \DomainException('mapbox_api_key_invalid');
}
}
} ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'mapbox', \IPS\Settings::i()->mapbox, FALSE, array(), $validation ) );
if ( $values = $form->values() )
{
if( $values['mapbox'] > 0 )
{
$values['googlemaps'] = 0;
$values['googleplacesautocomplete'] = 0;
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_MapboxMaps' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
\IPS\Output::i()->sidebar['actions'] = array(
'help' => array(
'title' => 'learn_more',
'icon' => 'question-circle',
'link' => \IPS\Http\Url::ips( 'docs/mapboxmaps' ),
'target' => '_blank'
),
);
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'enhancements__core_MapboxMaps', $form );
}
/**
* Enable/Disable
*
* @param $enabled bool Enable/Disable
* @return void
*/
public function toggle( $enabled )
{
/* If we're disabling, just disable */
if( !$enabled )
{
\IPS\Settings::i()->changeValues( array( 'mapbox' => 0 ) );
}
/* Otherwise if we already have an API key, just toggle on */
if( $enabled && \IPS\Settings::i()->mapbox_api_key )
{
\IPS\Settings::i()->changeValues( array( 'mapbox' => 1, 'googlemaps' => 0, 'googleplacesautocomplete' => 0 ) );
}
else
{
/* Otherwise we need to let them enter an API key before we can enable. Throwing an exception causes you to be redirected to the settings page. */
throw new \DomainException;
}
}
}
@@ -76,18 +76,23 @@ class _SendGrid
$form->add( new \IPS\Helpers\Form\YesNo( 'sendgrid_click_tracking', \IPS\Settings::i()->sendgrid_click_tracking, FALSE, array(), NULL, NULL, NULL, 'sendgrid_click_tracking' ) );
$form->add( new \IPS\Helpers\Form\Text( 'sendgrid_ip_pool', \IPS\Settings::i()->sendgrid_ip_pool ?: NULL, FALSE, array( 'nullLang' => 'sendgrid_ip_pool_none' ), NULL, NULL, NULL, 'sendgrid_ip_pool' ) );
if ( $form->values() )
if ( $values = $form->values() )
{
try
{
$this->testSettings( $form->values() );
$this->testSettings( $values );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( $e->getMessage(), '2C339/1', 500 );
}
$form->saveAsSettings();
if( $values['sendgrid_use_for'] > 0 )
{
$values['sparkpost_use_for'] = 0;
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_SendGrid' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
@@ -116,15 +121,13 @@ class _SendGrid
/* If we're disabling, just disable */
if( !$enabled )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'sendgrid_use_for' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'sendgrid_use_for' => 0 ) );
}
/* Otherwise if we already have an API key, just toggle bulk mail on */
if( $enabled && \IPS\Settings::i()->sendgrid_api_key )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'sendgrid_use_for' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'sendgrid_use_for' => 1, 'sparkpost_use_for' => 0 ) );
}
else
{
@@ -147,8 +150,7 @@ class _SendGrid
{
if( \IPS\Settings::i()->mail_method == 'sendgrid' )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 'mail' ), array( 'conf_key=?', 'mail_method' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'mail_method' => 'mail' ) );
}
return;
@@ -50,7 +50,7 @@ class _SpamMonitoring
public function __construct()
{
$licenseData = \IPS\IPS::licenseKey();
if( !$licenseData or !isset( $licenseData['products']['spam'] ) or !$licenseData['products']['spam'] or strtotime( $licenseData['expires'] ) < time() )
if( !$licenseData or !isset( $licenseData['products']['spam'] ) or !$licenseData['products']['spam'] or ( !$licenseData['cloud'] AND strtotime( $licenseData['expires'] ) < time() ) )
{
$this->enabled = false;
}
@@ -77,7 +77,7 @@ class _SpamMonitoring
}
catch ( \LogicException $e )
{
//Do not show error message. We will see error message in Settings
// \IPS\Output::i()->error( $e->getMessage(), '2C116/2', 403, '' );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=spam&tab=service' ) );
@@ -97,8 +97,7 @@ class _SpamMonitoring
$this->testSettings();
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $enabled ), array( 'conf_key=?', 'spam_service_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'spam_service_enabled' => $enabled ) );
}
/**
@@ -115,7 +114,7 @@ class _SpamMonitoring
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('spam_service_nokey', FALSE, array( 'sprintf' => array( \IPS\Http\Url::internal( 'app=core&module=settings&controller=licensekey', null ) ) ) ) );
}
if ( strtotime( $licenseData['expires'] ) < time() )
if ( !$licenseData['cloud'] AND strtotime( $licenseData['expires'] ) < time() )
{
throw new \DomainException('licensekey_expired');
}
@@ -1,198 +1,3 @@
<?php
/**
* @brief Community Enhancements: Sparkpost integration
* @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 29 March 2016
*/
namespace IPS\core\extensions\core\CommunityEnhancements;
/* 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;
}
/**
* Community Enhancements: Sparkpost integration
*/
class _Sparkpost
{
/**
* @brief IPS-provided enhancement?
*/
public $ips = FALSE;
/**
* @brief Enhancement is enabled?
*/
public $enabled = FALSE;
/**
* @brief Enhancement has configuration options?
*/
public $hasOptions = TRUE;
/**
* @brief Icon data
*/
public $icon = "sparkpost.png";
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$this->enabled = ( \IPS\Settings::i()->sparkpost_api_key && \IPS\Settings::i()->sparkpost_use_for );
}
/**
* Edit
*
* @return void
*/
public function edit()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Radio( 'sparkpost_use_for', \IPS\Settings::i()->sparkpost_use_for, TRUE, array(
'options' => array(
'0' => 'sparkpost_donot_use',
'1' => 'sparkpost_bulkmail_use',
'2' => 'sparkpost_all_use'
),
'toggles' => array(
'0' => array(),
'1' => array( 'sparkpost_api_key', 'sparkpost_click_tracking', 'sparkpost_ip_pool' ),
'2' => array( 'sparkpost_api_key', 'sparkpost_click_tracking', 'sparkpost_ip_pool' ),
)
) ) );
$form->add( new \IPS\Helpers\Form\Text( 'sparkpost_api_key', \IPS\Settings::i()->sparkpost_api_key, FALSE, array(), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('sparkpost_api_key_suffix'), 'sparkpost_api_key' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'sparkpost_click_tracking', \IPS\Settings::i()->sparkpost_click_tracking, FALSE, array(), NULL, NULL, NULL, 'sparkpost_click_tracking' ) );
$form->add( new \IPS\Helpers\Form\Text( 'sparkpost_ip_pool', \IPS\Settings::i()->sparkpost_ip_pool ?: NULL, FALSE, array( 'nullLang' => 'sparkpost_ip_pool_none' ), NULL, NULL, NULL, 'sparkpost_ip_pool' ) );
if ( $form->values() )
{
try
{
$this->testSettings( $form->values() );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( $e->getMessage(), '2S123/1', 500 );
}
$form->saveAsSettings();
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_Sparkpost' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
\IPS\Output::i()->sidebar['actions'] = array(
'help' => array(
'title' => 'learn_more',
'icon' => 'question-circle',
'link' => \IPS\Http\Url::ips( 'docs/sparkpost' ),
'target' => '_blank'
),
);
\IPS\Output::i()->output = $form;
}
/**
* Enable/Disable
*
* @param $enabled bool Enable/Disable
* @return void
* @throws \DomainException
*/
public function toggle( $enabled )
{
/* If we're disabling, just disable */
if( !$enabled )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'sparkpost_use_for' ) );
unset( \IPS\Data\Store::i()->settings );
}
/* Otherwise if we already have an API key, just toggle bulk mail on */
if( $enabled && \IPS\Settings::i()->sparkpost_api_key )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'sparkpost_use_for' ) );
unset( \IPS\Data\Store::i()->settings );
}
else
{
/* Otherwise we need to let them enter an API key before we can enable. Throwing an exception causes you to be redirected to the settings page. */
throw new \DomainException;
}
}
/**
* Test Settings
*
* @param array $values Form values
* @return void
* @throws \LogicException
*/
protected function testSettings( $values )
{
/* If we've disabled, just shut off */
if( (int) $values['sparkpost_use_for'] === 0 )
{
if( \IPS\Settings::i()->mail_method == 'sparkpost' )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 'mail' ), array( 'conf_key=?', 'mail_method' ) );
unset( \IPS\Data\Store::i()->settings );
}
return;
}
/* If we enable Sparkpost but do not supply an API key, this is a problem */
if( !$values['sparkpost_api_key'] )
{
throw new \InvalidArgumentException( "sparkpost_enable_need_details" );
}
/* Test Sparkpost settings */
try
{
$domain = mb_substr( \IPS\Settings::i()->email_out, mb_strpos( \IPS\Settings::i()->email_out, '@' ) + 1 );
$sparkpost = new \IPS\Email\Outgoing\SparkPost( $values['sparkpost_api_key'] );
$sendingDomains = $sparkpost->sendingDomains();
if ( isset( $sendingDomains['errors'] ) )
{
throw new \DomainException('sparkpost_bad_credentials');
}
$isOkay = FALSE;
foreach ( $sendingDomains['results'] as $sendingDomain )
{
if ( $sendingDomain['domain'] == $domain )
{
if ( $sendingDomain['status']['ownership_verified'] )
{
$isOkay = TRUE;
}
else
{
throw new \DomainException('sparkpost_domain_not_verified');
}
}
}
if ( !$isOkay )
{
throw new \DomainException('sparkpost_domain_not_registered');
}
}
catch ( \IPS\Http\Request\Exception $e )
{
throw new \DomainException( 'sparkpost_bad_credentials' );
}
}
}
// this file intentionally left blank
@@ -201,15 +201,13 @@ class _Viglink
/* If we're disabling, just disable */
if( !$enabled )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'viglink_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'viglink_enabled' => 0 ) );
}
/* Otherwise if we already have an API key, just toggle on */
if( $enabled && \IPS\Settings::i()->viglink_api_key )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'viglink_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'viglink_enabled' => 1 ) );
}
/* Otherwise we need to let them enter an API key before we can enable. Throwing an exception causes you to be redirected to the settings page. */
else
@@ -76,7 +76,7 @@ class _Email
$sender = explode( ',', \IPS\Settings::i()->contact_emails );
}
$mail = \IPS\Email::buildFromTemplate( 'core', 'contact_form', array( \IPS\Member::loggedIn(), $fromName, $fromEmail, $content ), \IPS\Email::TYPE_TRANSACTIONAL );
$mail->send( $sender , array(), array(), NULL, $fromName, array( 'Reply-To' => \IPS\Email::encodeHeader( $fromName, ( \IPS\Member::loggedIn()->member_id ? \IPS\Member::loggedIn()->email : $values['email_address'] ) ) ) );
$mail->send( $sender , array(), array(), NULL, $fromName, array( 'Reply-To' => \IPS\Email::encodeHeader( $fromName, ( \IPS\Member::loggedIn()->member_id ? \IPS\Member::loggedIn()->email : $values['email_address'] ) ) ), FALSE );
return TRUE;
}
@@ -40,13 +40,11 @@ class _AdminNotes
public function getBlock()
{
$form = new \IPS\Helpers\Form( 'form', 'save', \IPS\Http\Url::internal( "app=core&module=overview&controller=dashboard&do=getBlock&appKey=core&blockKey=core_AdminNotes" )->csrf() );
$form->add( new \IPS\Helpers\Form\TextArea( 'admin_notes', ( isset( \IPS\Settings::i()->acp_notes ) ) ? htmlspecialchars( \IPS\Settings::i()->acp_notes, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) : '' ) );
$form->add( new \IPS\Helpers\Form\TextArea( 'admin_notes', ( isset( \IPS\Settings::i()->acp_notes ) ) ? htmlspecialchars( \IPS\Settings::i()->acp_notes, ENT_DISALLOWED, 'UTF-8', FALSE ) : '' ) );
if( $values = $form->values() )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $values['admin_notes'] ), array( 'conf_key=?', 'acp_notes' ) );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'acp_notes_updated' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'acp_notes' => $values['admin_notes'], 'acp_notes_updated' => time() ) );
if( \IPS\Request::i()->isAjax() )
{
@@ -40,8 +40,8 @@ class _BackgroundQueue
public function getBlock()
{
$rows = array();
$select = \IPS\Db::i()->select( '*', 'core_queue', NULL, 'priority ASC, date ASC' );
$select = \IPS\Db::i()->select( '*', 'core_queue', array( 'app_enabled=?', 1 ), 'priority ASC, date ASC' )->join( 'core_applications', "app=app_directory" );
if ( count( $select ) )
{
foreach ( $select as $queueData )
@@ -1,94 +1,18 @@
<?php
/**
* @brief Dashboard extension: Show a notice if there are failed IPS Connect communications
* @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 03 Dec 2013
*/
// Removed
namespace IPS\core\extensions\core\Dashboard;
/* 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;
}
/**
* @brief Dashboard extension: Show a notice if there are failed IPS Connect communications
*/
class _IpsconnectMaster
{
/**
* Can the current user view this dashboard item?
*
* @return bool
*/
public function canView()
{
$handlers = \IPS\Login::handlers( TRUE );
return (bool) ( array_key_exists( 'Ipsconnect', $handlers ) and $handlers['Ipsconnect']->enabled );
return FALSE;
}
/**
* Return the block HTML show on the dashboard
*
* @return string
* @note We may want to consider doing something when there are failed queued requests but the slave record no longer exists. For now I don't want to automatically
clear them because it may be indicative of a bug elsewhere, but down the road we may want to simply delete the requests from the queue table and not show the failure.
*/
public function getBlock()
{
/* Are we deleting the slave? */
if( \IPS\Request::i()->deleteSlave )
{
\IPS\Session::i()->csrfCheck();
\IPS\Db::i()->delete( 'core_ipsconnect_queue', array( 'slave_id=?', intval( \IPS\Request::i()->deleteSlave ) ) );
\IPS\Db::i()->delete( 'core_ipsconnect_slaves', array( 'slave_id=?', intval( \IPS\Request::i()->deleteSlave ) ) );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => \IPS\Db::i()->select( 'COUNT(*)', 'core_ipsconnect_slaves', array( 'slave_enabled=?', 1 ) )->first() ), array( 'conf_key=?', 'connect_slaves' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('connect_slave_deleted');
\IPS\Session::i()->log( 'acplog__slave_deleted' );
}
/* Do we have any fails to show? */
$fails = \IPS\Db::i()->select( 'DISTINCT(slave_id)', 'core_ipsconnect_queue' );
if( count($fails) )
{
$failures = array();
foreach( $fails as $slave )
{
try
{
$slave = \IPS\Db::i()->select( '*', 'core_ipsconnect_slaves', array( 'slave_id=? AND slave_enabled=?', $slave, 1 ) )->first();
$fails = count( \IPS\Db::i()->select( 'id', 'core_ipsconnect_queue', array( 'slave_id=?', $slave['slave_id'] ) ) );
$failures[] = array(
'slave' => $slave,
'count' => $fails
);
}
catch( \UnderflowException $e )
{
/* This means that the slave record doesn't actually exist or is disabled */
}
}
if( count( $failures ) )
{
return \IPS\Theme::i()->getTemplate( 'dashboard' )->connectFailures( $failures, count($failures) );
}
}
else
{
return \IPS\Theme::i()->getTemplate( 'dashboard' )->noConnectFailures();
}
return '';
}
}
@@ -39,7 +39,7 @@ class _LatestNews
*/
public function getBlock()
{
return NULL;
//
}
/**
@@ -50,6 +50,9 @@ class _LatestNews
*/
protected function refreshNews()
{
return NULL;
\IPS\Data\Store::i()->ips_news = json_encode( array(
'content' => \IPS\Http\Url::ips( 'news' )->request()->get()->decodeJson(),
'time' => time()
) );
}
}
@@ -49,7 +49,7 @@ class _OnlineUsers
/* Add Rows */
$online = array();
$seen = array();
foreach( \IPS\Db::i()->select( "*", 'core_sessions', array( 'running_time>?', \IPS\DateTime::create()->sub( new \DateInterval('PT30M') )->getTimestamp() ), 'running_time DESC' ) as $row )
foreach( \IPS\Session\Store::i()->getOnlineUsers( \IPS\Session\Store::ONLINE_MEMBERS | \IPS\Session\Store::ONLINE_GUESTS, 'desc' ) as $row )
{
$key = ( $row['member_id'] ? $row['member_id'] : $row['id'] );
@@ -29,7 +29,7 @@ class _Registrations
*/
public function canView()
{
return TRUE;
return \IPS\Member::loggedIn()->hasAcpRestriction( 'core' , 'members', 'registrations_manage' );
}
/**
@@ -39,80 +39,31 @@ class _Registrations
*/
public function getBlock()
{
/* Init Chart */
$chart = new \IPS\Helpers\Chart;
/* Specify headers */
$chart->addHeader( \IPS\Member::loggedIn()->language()->addToStack('date'), 'string' ); // Since we're displaying a column chart, it makes more sense to use a string so they plotted as discreet columns rather than in the middle of dates
$chart->addHeader( \IPS\Member::loggedIn()->language()->addToStack('members'), 'number' );
$data = array();
/* We use midnight as a basis so we can include todays registrations */
$date = new \IPS\DateTime( 'tomorrow', new \DateTimeZone( \IPS\Member::loggedIn()->timezone ) );
/* We need to clone $date here for use in the query later */
$today = clone $date;
/* And we do this over a period of seven days - the query will also do this on the cloned object later. */
$date->sub( new \DateInterval( 'P7D' ) );
while ( $date->getTimestamp() < time() )
{
$data[ $date->format( 'Y-n-j' ) ] = 0;
$date->add( new \DateInterval( 'P1D' ) );
}
/* We can use the registration stats controller for this */
$chart = new \IPS\Helpers\Chart\Database(
\IPS\Http\Url::internal( 'app=core&module=stats&controller=registrationstats' ),
'core_members',
'joined',
'',
array(
'isStacked' => FALSE,
'backgroundColor' => '#ffffff',
'colors' => array( '#10967e' ),
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
'lineWidth' => 1,
'areaOpacity' => 0.4
),
'ColumnChart',
'weekly',
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P30D' ) ), 'end' => \IPS\DateTime::ts( time() ) )
);
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_new_registrations'), 'number', 'COUNT(*)', FALSE );
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
/* fetch only successful registered members ; if this needs to be changed, please review the other areas where we have the name<>? AND email<>? condition */
$where = array( 'joined>? AND name<>? AND email<>?', $today->sub( new \DateInterval( 'P7D' ) )->getTimestamp(), '', '' );
/* Add Rows */
foreach( \IPS\Db::i()->select( 'joined', 'core_members', $where, 'joined DESC' ) AS $joined )
{
$time = \IPS\DateTime::ts( $joined )->format( 'Y-n-j' );
/* Occasionally, timezone differences mean that we fall just over the beginning or end, which results in an undefined index. We can
ignore any registrations for dates that haven't been initialized. */
if( isset( $data[ $time ] ) )
{
$data[ $time ] += 1;
}
}
uksort( $data, function( $a, $b )
{
return strnatcmp( $a, $b );
} );
/* Add to graph */
foreach ( $data as $time => $d )
{
$datetime = new \IPS\DateTime;
$exploded = explode( '-', $time );
$datetime->setDate( $exploded[0], $exploded[1], $exploded[2] );
$chart->addRow( array( $datetime->format( 'j M Y' ), $d ) );
}
/* Work out the ticks */
$ticks = array();
$increment = ceil( max($data) / 5 );
for ($i = 1; $i <= 5; $i++)
{
$v = $increment * $i;
$ticks[] = array( 'v' => $v, 'f' => (string) $v );
}
$chart->where[] = array( '( name<>? AND email<>? )', '', '' );
/* Output */
return \IPS\Theme::i()->getTemplate( 'dashboard' )->registrations( $chart->render( 'ColumnChart', array(
'legend' => array( 'position' => 'none' ),
'areaOpacity' => 0.4,
'lineWidth' => 1,
'backgroundColor' => '#ffffff',
'colors' => array( '#10967e' ),
'vAxis' => array( 'ticks' => $ticks ),
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
) ) );
return \IPS\Theme::i()->getTemplate( 'dashboard' )->registrations( $chart );
}
}
@@ -238,8 +238,7 @@ class _Admin
if( $newMessage !== FALSE )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $newMessage ), "conf_key='site_offline_message'" );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'site_offline_message' => $newMessage ) );
}
/* Application disabled messages */
@@ -0,0 +1,87 @@
<?php
/**
* @brief Editor Extension: Review Responses
* @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 08 Jan 2018
*/
namespace IPS\core\extensions\core\EditorLocations;
/* 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;
}
/**
* Editor Extension: Review Responses
*/
class _ReviewResponses
{
/**
* Can we use HTML in this editor?
*
* @param \IPS\Member $member The member
* @return bool|null NULL will cause the default value (based on the member's permissions) to be used, and is recommended in most cases. A boolean value will override that.
*/
public function canUseHtml( $member )
{
return NULL;
}
/**
* Can we use attachments in this editor?
*
* @param \IPS\Member $member The member
* @param \IPS\Helpers\Form\Editor $field The editor field
* @return bool|null NULL will cause the default value (based on the member's permissions) to be used, and is recommended in most cases. A boolean value will override that.
*/
public function canAttach( $member, $field )
{
return NULL;
}
/**
* Permission check for attachments
*
* @param \IPS\Member $member The member
* @param int|null $id1 Primary ID
* @param int|null $id2 Secondary ID
* @param string|null $id3 Arbitrary data
* @param array $attachment The attachment data
* @return bool
*/
public function attachmentPermissionCheck( $member, $id1, $id2, $id3, $attachment )
{
try
{
$class = $id3 . '\\Review';
$item = $class::load( $id1 );
return $item->canView( $member );
}
catch ( \OutOfRangeException $e )
{
return FALSE;
}
}
/**
* Attachment lookup
*
* @param int|null $id1 Primary ID
* @param int|null $id2 Secondary ID
* @param string|null $id3 Arbitrary data
* @return \IPS\Http\Url|\IPS\Content|\IPS\Node\Model
* @throws \LogicException
*/
public function attachmentLookup( $id1, $id2, $id3 )
{
$class = $id3 . '\\Review';
return $class::load( $id2 );
}
}
@@ -0,0 +1,251 @@
<?php
/**
* @brief Editor Extension: Widget
* @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 13 Feb 2018
*/
namespace IPS\core\extensions\core\EditorLocations;
/* 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;
}
/**
* Editor Extension: Widget, Used for all the core widgets which need an editor
*/
class _Widget
{
/**
* Array containing all widgets utilizing this Extension
*/
protected static $widgets = array(
'guestSignUp',
'newsletter',
);
/**
* Can we use HTML in this editor?
*
* @param \IPS\Member $member The member
* @return bool|null NULL will cause the default value (based on the member's permissions) to be used, and is recommended in most cases. A boolean value will override that.
*/
public function canUseHtml( $member )
{
return NULL;
}
/**
* Can we use attachments in this editor?
*
* @param \IPS\Member $member The member
* @param \IPS\Helpers\Form\Editor $field The editor field
* @return bool|null NULL will cause the default value (based on the member's permissions) to be used, and is recommended in most cases. A boolean value will override that.
*/
public function canAttach( $member, $field )
{
return NULL;
}
/**
* Permission check for attachments
*
* @param \IPS\Member $member The member
* @param int|null $id1 Primary ID
* @param int|null $id2 Secondary ID
* @param string|null $id3 Arbitrary data
* @param array $attachment The attachment data
* @return bool
*/
public function attachmentPermissionCheck( $member, $id1, $id2, $id3, $attachment )
{
return TRUE;
}
/**
* Attachment lookup
*
* @param int|null $id1 Primary ID
* @param int|null $id2 Secondary ID
* @param string|null $id3 Arbitrary data
* @return \IPS\Http\Url|\IPS\Content|\IPS\Node\Model
* @throws \LogicException
*/
public function attachmentLookup( $id1, $id2, $id3 )
{
return NULL;
}
/**
* Rebuild attachment images in non-content item areas
*
* @param int|null $offset Offset to start from
* @param int|null $max Maximum to parse
* @return int Number completed
* @note This method is optional and will only be called if it exists
*/
public function rebuildAttachmentImages( $offset, $max )
{
return $this->performRebuild( $offset, $max, array( 'IPS\Text\Parser', 'rebuildAttachmentUrls' ) );
}
/**
* Rebuild content post-upgrade
*
* @param int|null $offset Offset to start from
* @param int|null $max Maximum to parse
* @return int Number completed
* @note This method is optional and will only be called if it exists
*/
public function rebuildContent( $offset, $max )
{
return $this->performRebuild( $offset, $max, array( 'IPS\Text\LegacyParser', 'parseStatic' ) );
}
/**
* @brief Store image proxy status ( true = enabled )
*/
protected $_imageProxyStatus = null;
/**
* Rebuild content to add or remove image proxy
*
* @param int|null $offset Offset to start from
* @param int|null $max Maximum to parse
* @param bool $status Enable/Disable Image Proxy
* @return int Number completed
* @note This method is optional and will only be called if it exists
*/
public function rebuildImageProxy( $offset, $max, $status=TRUE )
{
$this->_imageProxyStatus = $status;
return $this->performRebuild( $offset, $max, 'parseImageProxy' );
}
/**
* Perform rebuild - abstracted as the call for rebuildContent() and rebuildAttachmentImages() is nearly identical
*
* @param int|null $offset Offset to start from
* @param int|null $max Maximum to parse
* @param callable $callback Method to call to rebuild content
* @return int Number completed
*/
protected function performRebuild( $offset, $max, $callback )
{
$did = 0;
$areas = array( 'core_widget_areas' );
if ( \IPS\Application::appIsEnabled('cms') )
{
$areas[] = 'cms_page_widget_areas';
}
foreach ( $areas as $table )
{
foreach ( \IPS\Db::i()->select( '*', $table ) as $area )
{
$did++;
$widgetsColumn = $table == 'core_widget_areas' ? 'widgets' : 'area_widgets';
$whereClause = $table == 'core_widget_areas' ? array( 'id=? AND area=?', $area['id'], $area['area'] ) : array( 'area_page_id=? AND area_area=?', $area['area_page_id'], $area['area_area'] );
$widgets = json_decode( $area[ $widgetsColumn ], TRUE );
$update = FALSE;
foreach ( $widgets as $k => $widget )
{
if ( in_array( $widget['key'], static::$widgets ) )
{
$appOrPlugin = isset( $widget['plugin'] ) ? \IPS\Plugin::load( $widget['plugin'] ) : \IPS\Application::load( $widget['app'] );
$widgetObject = \IPS\Widget::load( $appOrPlugin, $widget['key'], $widget['unique'] );
$key = $widgetObject::$editorKey;
if ( isset( $widget['configuration'][$key ] ) )
{
try
{
if( $callback == 'parseImageProxy' )
{
$rebuilt = \IPS\Text\Parser::parseImageProxy( $widget['configuration'][$key ], $this->_imageProxyStatus );
}
else
{
$rebuilt = call_user_func( $callback, $widget['configuration'][$key ] );
}
}
catch( \InvalidArgumentException $e )
{
if( $callback[1] == 'parseStatic' AND $e->getcode() == 103014 )
{
$rebuilt = preg_replace( "#\[/?([^\]]+?)\]#", '', $widget['configuration'][$key ] );
}
else
{
throw $e;
}
}
if ( $rebuilt )
{
$widgets[ $k ]['configuration'][$key] =$rebuilt;
$update = TRUE;
}
}
}
if ( $update )
{
\IPS\Db::i()->update( $table, array( $widgetsColumn => json_encode( $widgets ) ), $whereClause );
}
}
}
}
return $did;
}
/**
* Total content count to be used in progress indicator
*
* @return int Total Count
*/
public function contentCount()
{
$count = 0;
$areas = array( 'core_widget_areas' );
if ( \IPS\Application::appIsEnabled('cms') )
{
$areas[] = 'cms_page_widget_areas';
}
foreach ( $areas as $table )
{
foreach ( \IPS\Db::i()->select( '*', $table ) as $area )
{
$widgetsColumn = $table == 'core_widget_areas' ? 'widgets' : 'area_widgets';
$widgets = json_decode( $area[ $widgetsColumn ], TRUE );
foreach ( $widgets as $k => $widget )
{
if ( in_array( $widget['key'], static::$widgets ) )
{
$count++;
}
}
}
}
return $count;
}
}
@@ -0,0 +1,135 @@
<?php
/**
* @brief File Storage Extension: ClubField
* @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 11 Aug 2017
*/
namespace IPS\core\extensions\core\FileStorage;
/* 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;
}
/**
* File Storage Extension: ClubField
*/
class _ClubField
{
/**
* Count stored files
*
* @return int
*/
public function count()
{
$count = 0;
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
$count += \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}<>? AND field_{$row['f_id']} IS NOT NULL", '' ) )->first();
}
return $count;
}
/**
* Move stored files
*
* @param int $offset This will be sent starting with 0, increasing to get all files stored by this extension
* @param int $storageConfiguration New storage configuration ID
* @param int|NULL $oldConfiguration Old storage configuration ID
* @throws \UnderflowException When file record doesn't exist. Indicating there are no more files to move
* @return void|int An offset integer to use on the next cycle, or nothing
*/
public function move( $offset, $storageConfiguration, $oldConfiguration=NULL )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}<>? AND field_{$row['f_id']} IS NOT NULL", '' ) ) AS $field )
{
try
{
$moved = \IPS\File::get( $oldConfiguration ?: 'core_ClubField', $field[ "field_{$row['f_id']}" ] )->move( $storageConfiguration );
\IPS\Db::i()->update( 'core_clubs_fieldvalues', array( "field_{$row['f_id']}" => (string) $moved ), array( "club_id=?", $field['club_id'] ) );
}
catch( \Exception $e )
{
// Any issues are logged
}
catch( \Throwable $e )
{
// Any issues are logged
}
}
}
throw new \UnderflowException;
}
/**
* Fix all URLs
*
* @param int $offset This will be sent starting with 0, increasing to get all files stored by this extension
* @return void
*/
public function fixUrls( $offset )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}<>? AND field_{$row['f_id']} IS NOT NULL" ) ) AS $field )
{
$new = \IPS\File::repairUrl( $field[ "field_{$row['f_id']}" ] );
\IPS\Db::i()->update( 'core_clubs_fieldvalues', array( "field_{$row['f_id']}" => $new ), array( "club_id=?", $field['club_id'] ) );
}
}
throw new \UnderflowException;
}
/**
* Check if a file is valid
*
* @param string $file The file path to check
* @return bool
*/
public function isValidFile( $file )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}=?", (string) $file ) )->first() )
{
return TRUE;
}
}
return FALSE;
}
/**
* Delete all stored files
*
* @return void
*/
public function delete()
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}<>? AND field_{$row['f_id']} IS NOT NULL" ) ) AS $field )
{
try
{
\IPS\File::get( 'core_ClubField', $field[ "field_{$row['f_id']}" ] )->delete();
}
catch( \OutOfRangeException $e ) {}
\IPS\Db::i()->update( 'core_clubs_fieldvalues', array( "field_{$row['f_id']}" => NULL ), array( "club_id=?", $field['club_id'] ) );
}
}
}
}
@@ -135,7 +135,7 @@ class _Clubs
{
if ( $new = \IPS\File::repairUrl( $club[ $photoKey ] ) )
{
$fixed[ $location ] = $new;
$fixed[ $photoKey ] = $new;
}
}
}
@@ -60,7 +60,8 @@ class _Emoticons
\IPS\Db::i()->update( 'core_emoticons', array( 'image' => (string) $file, 'image_2x' => (string) $image_2x ), array( 'id=?', $emoticon['id'] ) );
}
unset( \IPS\Data\Store::i()->emoticons );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
}
catch( \Exception $e )
{
@@ -93,7 +94,8 @@ class _Emoticons
{
\IPS\Db::i()->update( 'core_emoticons', $fixed, array( 'id=?', $emoticon['id'] ) );
unset( \IPS\Data\Store::i()->emoticons );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
}
}
catch( \Exception $e ) { }
@@ -45,6 +45,12 @@ class _Imageproxycache
{
$cache = \IPS\Db::i()->select( '*', 'core_image_proxy', array(), 'md5_url', array( $offset, 1 ) )->first();
/* Don't move a file if it doesn't have a filename */
if( $cache['location'] === NULL )
{
return;
}
try
{
$file = \IPS\File::get( $oldConfiguration ?: 'core_Imageproxycache', $cache['location'] )->move( $storageConfiguration );
@@ -0,0 +1,111 @@
<?php
/**
* @brief File Storage Extension: Login Methods
* @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 31 May 2017
*/
namespace IPS\core\extensions\core\FileStorage;
/* 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;
}
/**
* File Storage Extension: Login Methods
*/
class _Login
{
/**
* Count stored files
*
* @return int
*/
public function count()
{
$count = 0;
foreach ( \IPS\Db::i()->select( '*', 'core_login_methods' ) as $method )
{
$settings = json_decode( $method['login_settings'], TRUE );
if ( isset( $settings['button_icon'] ) and $settings['button_icon'] )
{
$count++;
}
}
return $count;
}
/**
* Move stored files
*
* @param int $offset This will be sent starting with 0, increasing to get all files stored by this extension
* @param int $storageConfiguration New storage configuration ID
* @param int|NULL $oldConfiguration Old storage configuration ID
* @throws \UnderflowException When file record doesn't exist. Indicating there are no more files to move
* @return void|int An offset integer to use on the next cycle, or nothing
*/
public function move( $offset, $storageConfiguration, $oldConfiguration=NULL )
{
foreach ( \IPS\Db::i()->select( '*', 'core_login_methods' ) as $method )
{
$settings = json_decode( $method['login_settings'], TRUE );
if ( isset( $settings['button_icon'] ) and $settings['button_icon'] )
{
try
{
$settings['button_icon'] = (string) \IPS\File::get( $oldConfiguration ?: 'core_Login', $settings['button_icon'] )->move( $storageConfiguration );
\IPS\Db::i()->update( 'core_login_methods', array( 'login_settings' => json_encode( $settings ) ), array( 'login_id=?', $method['login_id'] ) );
}
catch( \Exception $e )
{
/* Any issues are logged */
}
}
}
unset( \IPS\Data\Store::i()->loginMethods );
throw new \UnderflowException;
}
/**
* Check if a file is valid
*
* @param string $file The file path to check
* @return bool
*/
public function isValidFile( $file )
{
foreach ( \IPS\Db::i()->select( '*', 'core_login_methods' ) as $method )
{
$settings = json_decode( $method['login_settings'], TRUE );
if ( isset( $settings['button_icon'] ) and $settings['button_icon'] == (string) $file )
{
return TRUE;
}
}
return FALSE;
}
/**
* Delete all stored files
*
* @return void
*/
public function delete()
{
foreach ( \IPS\Db::i()->select( '*', 'core_login_methods' ) as $method )
{
$settings = json_decode( $method['login_settings'], TRUE );
if ( isset( $settings['button_icon'] ) and $settings['button_icon'] == (string) $file )
{
\IPS\File::get( 'core_Login', $settings['button_icon'] )->delete();
}
}
}
}
@@ -144,7 +144,7 @@ class _Profile
\IPS\File::get( 'core_Profile', $member['pp_cover_photo'] )->delete();
}
if ( mb_substr( $member['pp_photo_type'], 0, 5 ) === 'sync-' or $member['pp_photo_type'] === 'custom' )
if ( mb_substr( $member['pp_photo_type'], 0, 5 ) === 'sync-' or $member['pp_photo_type'] === 'custom' or $member['pp_photo_type'] === 'letter' )
{
if( $member['pp_main_photo'] )
{
@@ -52,7 +52,7 @@ class _Promote
{
try
{
\IPS\File::get( $oldConfiguration ?: 'core_Profile', $location )->move( $storageConfiguration );
\IPS\File::get( $oldConfiguration ?: 'core_Promote', $location )->move( $storageConfiguration );
}
catch( \Exception $e )
{
@@ -36,6 +36,9 @@ class _Theme
{
/* Clear out CSS as custom URL may have changed */
\IPS\Theme::deleteCompiledCss();
/* Trash this JS */
\IPS\Output::clearJsFiles();
}
/**
@@ -52,7 +52,7 @@ class _Clubs extends \IPS\core\FrontNavigation\FrontNavigationAbstract
*/
public function canAccessContent()
{
return \IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'online' ) );
return \IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'clubs' ) );
}
/**
@@ -31,15 +31,19 @@ class _Content
*/
public function process( &$form, $group )
{
$contentClasses = array();
/* Set up available content classes for customizations - add in conversations which do not have a content router extension */
$contentClasses = array( 'IPS\core\Messenger\Conversation' => 'personal_conversation_pl' );
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter', $group ) as $class )
{
foreach ( $class->classes as $class )
{
$contentClasses[ $class ] = $class::$title . '_pl';
if ( isset( $class::$databaseColumnMap['author'] ) )
{
$contentClasses[ $class ] = $class::$title . '_pl';
}
}
}
/* Uploading */
if ( \IPS\Settings::i()->attach_allowed_types != 'none' )
{
@@ -47,9 +51,9 @@ class _Content
$form->add( new \IPS\Helpers\Form\YesNo( 'g_attach', ( $group->g_attach_max != 0 ), FALSE, array( 'togglesOn' => array( 'g_attach_max', 'g_attach_per_post', 'gbw_delete_attachments' ) ) ) );
if( $group->g_id != \IPS\Settings::i()->guest_group )
{
$form->add( new \IPS\Helpers\Form\Number( 'g_attach_max', $group->g_attach_max, FALSE, array( 'unlimited' => -1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ), 'g_attach_max' ) );
$form->add( new \IPS\Helpers\Form\Number( 'g_attach_max', $group->g_id ? $group->g_attach_max : 500000, FALSE, array( 'unlimited' => -1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ), 'g_attach_max' ) );
}
$form->add( new \IPS\Helpers\Form\Number( 'g_attach_per_post', $group->g_attach_per_post, FALSE, array( 'unlimited' => 0 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ), 'g_attach_per_post' ) );
$form->add( new \IPS\Helpers\Form\Number( 'g_attach_per_post', $group->g_id ? $group->g_attach_per_post : 50000, FALSE, array( 'unlimited' => 0 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ), 'g_attach_per_post' ) );
if( $group->g_id != \IPS\Settings::i()->guest_group )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'gbw_delete_attachments', $group->g_bitoptions['gbw_delete_attachments'], FALSE, array(), NULL, NULL, NULL, 'gbw_delete_attachments' ) );
@@ -134,6 +138,8 @@ class _Content
if( $group->g_id != \IPS\Settings::i()->guest_group )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'gbw_immune_auto_mod', $group->g_bitoptions['gbw_immune_auto_mod'] ) );
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'g_lock_unlock_own', !$group->g_lock_unlock_own ? 0 : ( $group->g_lock_unlock_own == '1' ? '1' : explode( ',', $group->g_lock_unlock_own ) ), FALSE, array(
'options' => array_filter( $contentClasses, function( $class ) {
return in_array( 'IPS\Content\Lockable', class_implements( $class ) );
@@ -144,7 +150,7 @@ class _Content
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'g_can_report', !$group->g_can_report ? 0 : ( $group->g_can_report == '1' ? '1' : explode( ',', $group->g_can_report ) ), FALSE, array(
'options' => array_filter( $contentClasses, function( $class ) {
return in_array( 'IPS\Content\ReportCenter', class_implements( $class ) );
return \IPS\IPS::classUsesTrait( $class, 'IPS\Content\Reportable' );
}, ARRAY_FILTER_USE_KEY ),
'unlimited' => '1'
) ) );
@@ -218,7 +224,7 @@ class _Content
$values['gbw_disable_tagging'] = \IPS\Settings::i()->tags_enabled ? !$values['gbw_disable_tagging'] : $group->g_bitoptions['gbw_disable_tagging'];
$values['gbw_disable_prefixes'] = ( \IPS\Settings::i()->tags_enabled AND \IPS\Settings::i()->tags_can_prefix ) ? !$values['gbw_disable_prefixes'] : $group->g_bitoptions['gbw_disable_prefixes'];
$bwKeys = array( 'gbw_disable_tagging', 'gbw_disable_prefixes', 'gbw_delete_attachments', 'gbw_post_highlight', 'gbw_promote' );
$bwKeys = array( 'gbw_disable_tagging', 'gbw_disable_prefixes', 'gbw_delete_attachments', 'gbw_post_highlight', 'gbw_promote', 'gbw_immune_auto_mod' );
foreach ( $bwKeys as $k )
{
@@ -67,7 +67,7 @@ class _GroupSettings
{
return array( 'prefix' => $element->value['prefix'], 'suffix' => $element->value['suffix'] );
}
elseif ( isset( $element->value['color'] ) )
elseif ( isset( $element->value['color'] ) AND $element->value['color'] )
{
$color = mb_strtolower( $element->value['color'] );
if ( mb_substr( $color, 0, 1 ) !== '#' )
@@ -101,7 +101,7 @@ class _GroupSettings
}
}
$form->add( new \IPS\Helpers\Form\YesNo( 'g_view_board', $group->g_id ? $group->g_view_board : TRUE, FALSE, array( 'togglesOn' => array_merge( $tabs, array( 'group_username', 'g_dohtml', 'g_use_search', "{$form->id}_header_group_privacy", 'g_hide_online_list', 'gbw_hide_group', "{$form->id}_header_group_signatures", 'g_use_signatures', "{$form->id}_header_group_staff", 'g_access_offline', 'g_search_flood', 'gbw_cannot_be_ignored' ) ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'g_view_board', $group->g_id ? $group->g_view_board : TRUE, FALSE, array( 'togglesOn' => array_merge( $tabs, array( 'group_username', 'g_dohtml', 'g_use_search', "gbw_cannot_be_ignored", "{$form->id}_header_group_signatures", 'g_use_signatures', "{$form->id}_header_group_staff", 'g_access_offline', 'g_search_flood', 'gbw_cannot_be_ignored' ) ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'g_access_offline', $group->g_access_offline, FALSE, array(), NULL, NULL, NULL, 'g_access_offline' ) );
/* Usernames */
@@ -130,24 +130,35 @@ class _GroupSettings
{
$value[0] = 0;
}
if ( isset( $value['unlimited'] ) AND $value['unlimited'] )
else
{
$value[0] = -1;
$value[3] = 0;
}
elseif ( !isset( $value[0] ) )
{
$value[0] = 0;
}
if ( isset( $value['always'] ) AND $value['always'] )
{
$value[1] = 0;
$value[2] = 0;
if ( isset( $value['unlimited'] ) AND $value['unlimited'] )
{
$value[0] = -1;
$value[3] = 0;
}
elseif ( !isset( $value[0] ) )
{
$value[0] = 0;
}
if ( isset( $value['always'] ) AND $value['always'] )
{
$value[1] = 0;
$value[2] = 0;
}
}
return $value;
},
'validate' => function( $element )
{
$value = $element->value;
if( ( isset( $value[1] ) AND $value[1] < 0 ) OR ( isset( $value[2] ) AND $value[2] < 0 ) OR ( isset( $value[3] ) AND $value[3] < 0 ) )
{
throw new \DomainException( 'only_positive_values' );
}
}
), NULL, NULL, NULL, 'group_username' ) );
}
@@ -240,7 +251,7 @@ class _GroupSettings
{
$group->g_dname_changes = ( isset( $values['group_username']['canchange'] ) and $values['group_username']['canchange'] ) ? $values['group_username'][0] : 0;
$group->g_displayname_unit = ( isset( $values['group_username']['canchange'] ) ) ? (int) $values['group_username'][1] : 0;
$group->g_bitoptions['gbw_displayname_unit_type'] = $values['group_username'][2];
$group->g_bitoptions['gbw_displayname_unit_type'] = ( isset( $values['group_username'][2] ) ) ? $values['group_username'][2] : 0;
$group->g_dname_date = ( isset( $values['group_username']['canchange'] ) ) ? $values['group_username'][3] : 1;
}
@@ -291,4 +302,48 @@ class _GroupSettings
$group->g_bitoptions['gbw_cannot_be_ignored'] = !$values['can_be_ignored'];
}
}
/**
* Delete
*
* @param \IPS\Member\Group $group The group
* @return void
*/
public function delete( $group )
{
if ( $group->g_icon )
{
try
{
\IPS\File::get( 'core_Theme', $group->g_icon )->delete();
}
catch( \Exception $ex ) { }
}
}
/**
* Copy Group
*
* @param \IPS\Member\Group $oldGroup The old, just cloned group
* @param \IPS\Member\Group $newGroup The new group
* @return void
*/
public function cloneGroup( $oldGroup, $newGroup )
{
if ( $oldGroup->g_icon )
{
try
{
$icon = \IPS\File::get( 'core_Theme', $oldGroup->g_icon );
$newIcon = \IPS\File::create( 'core_Theme', $icon->originalFilename, $icon->contents() );
$newGroup->g_icon = (string) $newIcon;
}
catch ( \Exception $e )
{
$newGroup->icon = NULL;
}
$newGroup->save();
}
}
}
@@ -83,9 +83,17 @@ class _Social
\IPS\Member\Club::TYPE_OPEN => 'club_type_open',
\IPS\Member\Club::TYPE_CLOSED => 'club_type_closed',
\IPS\Member\Club::TYPE_PRIVATE => 'club_type_private',
\IPS\Member\Club::TYPE_READONLY => 'club_type_readonly',
),
), NULL, NULL, NULL, 'g_create_clubs' ) );
if ( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'gbw_paid_clubs', $group->g_id ? ( $group->g_bitoptions['gbw_paid_clubs'] ) : FALSE ) );
}
$form->add( new \IPS\Helpers\Form\Number( 'g_club_limit', $group->g_club_limit ?: -1, FALSE, array( 'unlimited' => -1 ) ) );
$availableClubNodes = array();
foreach ( \IPS\Member\Club::availableNodeTypes() as $class )
{
@@ -176,6 +184,12 @@ class _Social
{
$group->g_create_clubs = implode( ',', $values['g_create_clubs'] );
$group->g_club_allowed_nodes = ( count( $values['g_club_allowed_nodes'] ) === count( \IPS\Member\Club::availableNodeTypes() ) ) ? '*' : implode( ',', $values['g_club_allowed_nodes'] );
$group->g_club_limit = $values['g_club_limit'] == -1 ? NULL : $values['g_club_limit'];
if ( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on )
{
$bwKeys[] = 'gbw_paid_clubs';
}
}
}
@@ -49,6 +49,7 @@ class _GroupLimits
'g_delete_own_posts' => array( $this, '_perAppSettings' ),
'g_lock_unlock_own' => array( $this, '_perAppSettings' ),
'g_can_report' => array( $this, '_perAppSettings' ),
'g_club_allowed_nodes' => array( $this, '_clubNodes' ),
)
);
}
@@ -134,7 +135,6 @@ class _GroupLimits
/* If signatures are currently enabled but aren't in the group we are checking, signatures should not be disabled */
elseif ( $index == 0 and $_cur[ $index ] < $values[ $index ] )
{
$_new[ $index ] = $_cur[ $index ];
}
else if( $_cur[ $index ] > $values[ $index ] )
@@ -145,6 +145,13 @@ class _GroupLimits
{
$_new[ $index ] = $values[ $index ];
}
/* If we are not allowed to use any images, ignore the dimensions specified.
Otherwise group A with 0 images and no limits on sizes and group B with 1 images at 200x200 results in 1 image allowed unlimited sizes. */
if( ( $index == 2 OR $index == 3 ) AND $values[1] == 0 )
{
$_new[ $index ] = $_cur[ $index ];
}
}
ksort($_new);
@@ -353,4 +360,26 @@ class _GroupLimits
);
}
}
/**
* Available node types for clubs
*
* @param array $a Group A's values
* @param array $b Group B's values
* @param string $k The key we want to get the combined value for
* @param array $member Member data
* @return mixed
*/
public function _clubNodes( $a, $b, $k, $member )
{
if ( $a[$k] == '*' or $b[$k] == '*' )
{
return '*';
}
/* We need to merge all content types */
$nodeTypes = array_unique( array_merge( explode( ",", $a[$k]), explode( ",", $b[$k]) ) );
return implode( ",", $nodeTypes );
}
}
@@ -94,7 +94,7 @@ class _Content extends \IPS\Content\ExtensionGenerator
try
{
$comment = $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] );
return "<a href='" . $comment->url() . "' target='_blank'>" . htmlspecialchars( $comment->item()->mapped('title'), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . '</a>';
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $comment->url(), TRUE, $comment->item()->mapped('title') );
}
catch ( \OutOfRangeException $e )
{
@@ -135,7 +135,7 @@ class _Content extends \IPS\Content\ExtensionGenerator
}
}
return "<a href='" . $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] )->url() . "' target='_blank'>" . htmlspecialchars( $val, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . '</a>';
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] )->url(), TRUE, $val );
},
);
if( isset( $class::$databaseColumnMap['container'] ) )
@@ -144,7 +144,7 @@ class _Content extends \IPS\Content\ExtensionGenerator
{
$nodeClass = $class::$containerNodeClass;
$node = $nodeClass::load( $val );
return "<a href='" . $node->url() . "' target='_blank'>" . htmlspecialchars( $node->_title, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . '</a>';
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $node->url(), TRUE, $node->_title );
};
}
@@ -97,6 +97,6 @@ class _MemberHistory
*/
public function findByMember( $member )
{
return \IPS\Db::i()->select( "log_ip_address AS ip, COUNT(*) AS count, MIN(log_date) AS first, MAX(log_date) AS last", 'core_member_history', array( 'log_member=?', $member->member_id ), NULL, NULL, 'log_ip_address' )->setKeyField( 'ip' );
return \IPS\Db::i()->select( "log_ip_address AS ip, COUNT(*) AS count, MIN(log_date) AS first, MAX(log_date) AS last", 'core_member_history', array( 'log_by=?', $member->member_id ), NULL, NULL, 'log_ip_address' )->setKeyField( 'ip' );
}
}
@@ -87,7 +87,7 @@ class _Ratings
{
$class = $row['class'];
$rated = $class::load( $row['item_id'] );
return "<a href='" . $rated->url() . "' target='_blank'>" . htmlspecialchars( $rated->mapped('title'), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $rated->url(), TRUE, $rated->mapped('title') );
}
catch ( \OutOfRangeException $e )
{
@@ -95,7 +95,7 @@ class _Registration
'name' => function( $val, $row )
{
$link = ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation === 'front' ) ? \IPS\Member::constructFromData( $row )->url() : \IPS\Member::constructFromData( $row )->acpUrl();
return "<a href='" . $link . "'>" . htmlentities( $val, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $link, TRUE, $val );
},
);
@@ -90,7 +90,7 @@ class _ReportComments
try
{
$report = \IPS\core\Reports\Report::load( $row['rid'] );
return "<a href='" . $report->url() . "' target='_blank'>" . htmlspecialchars( $report->mapped('title'), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $report->url(), TRUE, $report->mapped('title') );
}
catch( \OutOfRangeException $e )
{
@@ -90,7 +90,7 @@ class _Reports
try
{
$report = \IPS\core\Reports\Report::load( $row['rid'] );
return "<a href='" . $report->url() . "' target='_blank'>" . htmlspecialchars( $report->mapped('title'), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $report->url(), TRUE, $reort->mapped('title') );
}
catch( \OutOfRangeException $e )
{
@@ -87,7 +87,7 @@ class _Validations
{
$member = \IPS\Member::load( $row['member_id'] );
$link = ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation === 'front' ) ? $member->url() : $member->acpUrl();
return "<a href='" . $link . "'>" . htmlentities( $member->name, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $link, FALSE, $member->name );
},
'validatingType' => function( $val, $row )
{
@@ -90,7 +90,7 @@ class _Votes
try
{
$poll = \IPS\Poll::load( $val );
return htmlspecialchars( $poll->poll_question, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
return htmlspecialchars( $poll->poll_question, ENT_DISALLOWED, 'UTF-8', FALSE );
}
catch ( \OutOfRangeException $e )
{
@@ -0,0 +1,89 @@
<?php
/**
* @brief ACP Member Profile: Basic Information Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Basic Information Block
*/
class _BasicInformation extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$hasPassword = FALSE;
$canChangePassword = \IPS\Login\Handler::findMethod( 'IPS\Login\Handler\Standard' );
$activeIntegrations = array();
foreach ( \IPS\Login::methods() as $method )
{
if ( $method->canProcess( $this->member ) )
{
if ( !( $method instanceof \IPS\Login\Handler\Standard ) )
{
$activeIntegrations[] = $method->_title;
}
if ( $method->canChangePassword( $this->member ) )
{
$hasPassword = TRUE;
$canChangePassword = TRUE;
}
}
}
$accountActions = array();
if ( \IPS\Member::loggedIn()->member_id != $this->member->member_id AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_login' ) AND !$this->member->isBanned() )
{
$accountActions[] = array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'login_as_x', FALSE, array( 'sprintf' => array( $this->member->name ) ) ),
'icon' => 'key',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=login&id={$this->member->member_id}" ),
'class' => '',
'target' => '_blank'
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'members_merge' ) )
{
$accountActions[] = array(
'title' => 'merge_with_another_account',
'icon' => 'level-up',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=merge&id={$this->member->member_id}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('merge') )
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_delete' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_delete_admin' ) or !$this->member->isAdmin() ) and $this->member->member_id != \IPS\Member::loggedIn()->member_id )
{
$accountActions[] = array(
'title' => 'delete',
'icon' => 'times-circle',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=delete&id={$this->member->member_id}" ),
'data' => array( 'delete' => '', 'delete-warning' => \IPS\Member::loggedIn()->language()->addToStack( 'member_delete_confirm_desc' ), 'noajax' => '' )
);
}
$activeSubscription = FALSE;
if ( \IPS\Application::appIsEnabled('nexus') and \IPS\Settings::i()->nexus_subs_enabled ) // I know... this should really be a hook... I won't tell if you won't
{
$activeSubscription = \IPS\nexus\Subscription::loadActiveByMember( $this->member );
}
return \IPS\Theme::i()->getTemplate('memberprofile')->basicInformation( $this->member, $canChangePassword, $hasPassword, $activeIntegrations, $accountActions, $activeSubscription );
}
}
@@ -0,0 +1,200 @@
<?php
/**
* @brief ACP Member Profile: Content Statistics Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Content Statistics
*/
class _ContentStatistics extends \IPS\core\MemberACPProfile\TabbedBlock
{
/**
* @brief Percentages
*/
protected $percentages = array();
/**
* @brief Raw Counts
*/
protected $rawCounts = array();
/**
* Get Block Title
*
* @return string
*/
public function blockTitle()
{
return 'content_statistics';
}
/**
* Constructor
*
* @param \IPS\Member $member Member
* @return void
*/
public function __construct( \IPS\Member $member )
{
parent::__construct( $member );
$classes = array();
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter' ) as $contentRouter )
{
foreach ( $contentRouter->classes as $class )
{
$include = FALSE;
$exploded = explode( '\\', $class );
if ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
if ( $class::incrementPostCount() )
{
$include = TRUE;
$classes[ $exploded[1] ]['items'][] = $class;
}
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
if ( $commentClass::incrementPostCount() )
{
$include = TRUE;
$classes[ $exploded[1] ]['comments'][] = $commentClass;
}
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
if ( $reviewClass::incrementPostCount() )
{
$include = TRUE;
$classes[ $exploded[1] ]['comments'][] = $class::$reviewClass;
}
}
}
elseif ( in_array( 'IPS\Content\Comment', class_parents( $class ) ) )
{
if ( $class::incrementPostCount() )
{
$include = TRUE;
$classes[ $exploded[1] ]['comments'][] = $class;
}
}
if ( $include and !isset( $this->percentages[ $exploded[1] ] ) )
{
$this->percentages[ $exploded[1] ] = 0;
}
}
}
$sum = 0;
$counts = array();
foreach ( $classes as $app => $types )
{
foreach ( $types as $type => $typeClasses )
{
foreach ( $typeClasses as $class )
{
if( !isset( $class::$databaseColumnMap['author'] ) )
{
continue;
}
$authorColumn = $class::$databasePrefix . $class::$databaseColumnMap['author'];
if ( !isset( $counts[ $app ][ $type ] ) )
{
$counts[ $app ][ $type ] = 0;
}
if ( !isset( $this->rawCounts[ $app ][ $class ] ) )
{
$this->rawCounts[ $app ][ $class ] = 0;
}
$count = $class::memberPostCount( $member );
$sum += $count;
$counts[ $app ][ $type ] += $count;
$this->rawCounts[ $app ][ $class ] += $count;
}
}
}
if ( $sum )
{
foreach ( $counts as $app => $types )
{
if ( array_sum( $types ) )
{
$this->percentages[ $app ] = round( 100 / $sum * array_sum( $types ), 2 );
}
}
}
}
/**
* Get Tab Names
*
* @return string
*/
public function tabs()
{
$return = array();
if ( array_sum( $this->percentages ) )
{
$return['breakdown'] = 'content_count_breakdown';
}
foreach ( \IPS\Application::allExtensions( 'core', 'MemberACPProfileContentTab', TRUE, NULL, NULL, FALSE ) as $class )
{
try
{
$ext = new $class( $this->member );
$exploded = explode( '\\', $class );
$return[ $exploded[1] . '_' . $exploded[5] ] = 'content_stats__' . $exploded[1] . '_' . $exploded[5];
}
catch ( \Exception $e ) { }
}
return $return;
}
/**
* Get output
*
* @return string
*/
public function tabOutput( $tab )
{
if ( $tab == 'breakdown' )
{
return \IPS\Theme::i()->getTemplate('memberprofile')->contentBreakdown( $this->member, $this->percentages, $this->rawCounts );
}
else
{
$exploded = explode( '_', $tab );
$class = 'IPS\\' . $exploded[0] . '\\extensions\\core\\MemberACPProfileContentTab\\' . $exploded[1];
if ( class_exists( $class ) )
{
$ext = new $class( $this->member );
return $ext->output();
}
return '';
}
}
}
@@ -0,0 +1,37 @@
<?php
/**
* @brief ACP Member Profile: Devices & IP Addresses Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Devices & IP Addresses Block
*/
class _DevicesAndIPAddresses extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$lastUsedIp = $this->member->lastUsedIp();
$devices = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members_known_devices', array( 'member_id=?', $this->member->member_id ), 'last_seen DESC', 5 ), 'IPS\Member\Device' );
return \IPS\Theme::i()->getTemplate('memberprofile')->devicesAndIPAddresses( $this->member, $lastUsedIp, $devices );
}
}
@@ -0,0 +1,120 @@
<?php
/**
* @brief ACP Member Profile: Groups Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Groups Block
*/
class _Groups extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$secondaryGroups = array();
foreach ( array_filter( array_map( "intval", explode( ',', $this->member->mgroup_others ) ) ) as $secondaryGroupId )
{
try
{
$secondaryGroups[] = \IPS\Member\Group::load( $secondaryGroupId );
}
catch ( \OutOfRangeException $e ) { }
}
return \IPS\Theme::i()->getTemplate('memberprofile')->groups( $this->member, $secondaryGroups );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
/* Check permission */
if ( $this->member->isAdmin() )
{
\IPS\Dispatcher::i()->checkAcpPermission( 'member_move_admin1' );
}
/* If we are editing ourselves, we can only move ourselves into a group with the same restrictions as what we have now... */
if ( $this->member->member_id == \IPS\Member::loggedIn()->member_id )
{
/* Get the row... */
try
{
$currentRestrictions = \IPS\Db::i()->select( 'row_perm_cache', 'core_admin_permission_rows', array( 'row_id=? AND row_id_type=?', $this->member->member_group_id, 'group' ) )->first();
$availableGroups = array();
foreach( \IPS\Db::i()->select( 'row_id', 'core_admin_permission_rows', array( 'row_perm_cache=? AND row_id_type=?', $currentRestrictions, 'group' ) ) AS $groupId )
{
$availableGroups[ $groupId ] = \IPS\Member\Group::load( $groupId );
}
}
/* If we don't have a row in core_admin_permission_rows, we're an admin as a member rather than apart of our group, so we can be moved anywhere and it won't matter because member-level restrictions override group-level */
catch ( \UnderflowException $e )
{
$availableGroups = \IPS\Member\Group::groups( TRUE, FALSE );
}
}
/* Not editing ourselves - do we have the Can move members into admin groups"" restriction? */
else
{
$availableGroups = \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE );
}
/* Build form */
$form = new \IPS\Helpers\Form( 'group_form', 'save', NULL, array(
'data-controller' => 'core.admin.members.form',
'data-adminGroups' => json_encode( iterator_to_array( \IPS\Db::i()->select( 'row_id', 'core_admin_permission_rows', array( 'row_id_type=?', 'group' ) ) ) )
)) ;
$form->add( new \IPS\Helpers\Form\Select( 'group', $this->member->member_group_id, TRUE, array( 'options' => $availableGroups, 'parse' => 'normal' ) ) );
$form->add( new \IPS\Helpers\Form\Select( 'secondary_groups', array_filter( explode( ',', $this->member->mgroup_others ) ), FALSE, array( 'options' => \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE ), 'multiple' => TRUE, 'parse' => 'normal' ) ) );
/* Handle submissions */
if ( $values = $form->values() )
{
$oldPrimary = $this->member->member_group_id;
$oldSecondary = array_filter( explode( ',', $this->member->mgroup_others ) );
$changes = array();
if ( $this->member->member_group_id != $values['group'] )
{
$this->member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'manual', 'old' => $this->member->member_group_id, 'new' => $values['group'] ) );
}
$currentSecondary = array_filter( explode( ',', $this->member->mgroup_others ) );
if ( array_diff( $currentSecondary, $values['secondary_groups'] ) or array_diff( $values['secondary_groups'], $currentSecondary ) )
{
$this->member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'manual', 'old' => $currentSecondary, 'new' => $values['secondary_groups'] ) );
}
$this->member->member_group_id = $values['group'];
$this->member->mgroup_others = implode( ',', $values['secondary_groups'] );
$this->member->save();
\IPS\Session::i()->log( 'acplog__members_edited_groups', array( $this->member->name => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,169 @@
<?php
/**
* @brief ACP Member Profile: Header Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Header Block
*/
class _Header extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
/* Sparkline */
$rows = array();
$cutoff = \IPS\DateTime::create()->setTime( 0, 0, 0 )->sub( new \DateInterval('P30D') );
$date = clone $cutoff;
while ( $date->getTimestamp() < time() )
{
$rows[ $date->format( 'Y-n-j' ) ] = 0;
$date->add( new \DateInterval( 'P1D' ) );
}
$sparkline = new \IPS\Helpers\Chart;
$classes = array();
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter' ) as $contentRouter )
{
foreach ( $contentRouter->classes as $class )
{
$classes[] = $class;
if ( isset( $class::$commentClass ) )
{
$classes[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$classes[] = $class::$reviewClass;
}
}
}
foreach ( $classes as $class )
{
if( !isset( $class::$databaseColumnMap['author'] ) )
{
continue;
}
$dateColumn = $class::$databasePrefix . ( isset( $class::$databaseColumnMap['updated'] ) ? $class::$databaseColumnMap['updated'] : $class::$databaseColumnMap['date'] );
$authorColumn = $class::$databasePrefix . $class::$databaseColumnMap['author'];
foreach ( \IPS\Db::i()->select( "COUNT(*) AS count, DATE_FORMAT( FROM_UNIXTIME( IFNULL( {$dateColumn}, 0 ) ), '%Y-%c-%e' ) as time", $class::$databaseTable, array( "{$dateColumn}>? AND {$authorColumn}=?", $cutoff->getTimestamp(), $this->member->member_id ), NULL, NULL, array( $authorColumn, $dateColumn ) ) as $row )
{
$rows[ $row['time'] ] += $row['count'];
}
}
$sparkline->addHeader( \IPS\Member::loggedIn()->language()->addToStack('date'), 'date' );
$sparkline->addHeader( \IPS\Member::loggedIn()->language()->addToStack('members_posts'), 'number' );
foreach ( $rows as $time => $val )
{
$datetime = new \IPS\DateTime;
$datetime->setTime( 0, 0, 0 );
$exploded = explode( '-', $time );
$datetime->setDate( $exploded[0], $exploded[1], $exploded[2] );
$sparkline->addRow( array( $datetime, $val ) );
}
$sparkline = $sparkline->render( 'AreaChart', array(
'areaOpacity' => 0.4,
'backgroundColor' => '#fafafa',
'colors' => array( '#10967e' ),
'chartArea' => array(
'left' => 0,
'top' => 0,
'width' => '100%',
'height' => '100%',
),
'hAxis' => array(
'baselineColor' => '#F3F3F3',
'gridlines' => array(
'count' => 0,
)
),
'height' => 60,
'legend' => array(
'position' => 'none',
),
'lineWidth' => 1,
'vAxis' => array(
'baselineColor' => '#F3F3F3',
'gridlines' => array(
'count' => 0,
)
),
) );
/* Validating */
$validatingRow = NULL;
if ( $this->member->members_bitoptions['validating'] )
{
try
{
$validatingRow = \IPS\Db::i()->select( '*', 'core_validating', array( 'member_id=?', $this->member->member_id ) )->first();
}
catch ( \UnderflowException $e ) { }
}
/* Output */
return \IPS\Theme::i()->getTemplate('memberprofile')->header( $this->member, $validatingRow, $sparkline );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
/* Build Form */
$form = new \IPS\Helpers\Form;
if ( \IPS\Request::i()->type == 'content' )
{
$form->add( new \IPS\Helpers\Form\Number( 'member_content_items', $this->member->member_posts, FALSE ) );
}
elseif ( \IPS\Settings::i()->reputation_enabled and \IPS\Request::i()->type == 'reputation' )
{
$form->add( new \IPS\Helpers\Form\Number( 'member_reputation', $this->member->pp_reputation_points, FALSE ) );
}
else
{
\IPS\Output::i()->error( 'node_error', '2C114/U', 404, '' );
}
/* Handle submissions */
if ( $values = $form->values() )
{
if ( \IPS\Request::i()->type == 'content' )
{
$this->member->member_posts = $values['member_content_items'];
\IPS\Session::i()->log( 'acplog__members_edited_content', array( $this->member->name => FALSE ) );
}
elseif ( \IPS\Settings::i()->reputation_enabled and \IPS\Request::i()->type == 'reputation' )
{
$this->member->pp_reputation_points = $values['member_reputation'];
\IPS\Session::i()->log( 'acplog__members_edited_rep', array( $this->member->name => FALSE ) );
}
$this->member->save();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,52 @@
<?php
/**
* @brief ACP Member Profile: Locations Map Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Locations Map Block
*/
class _Locations extends \IPS\core\MemberACPProfile\LazyLoadingBlock
{
/**
* Get output
*
* @return string
*/
public function lazyOutput()
{
$mapMarkers = array();
if ( \IPS\Settings::i()->ipsgeoip and \IPS\Settings::i()->googlemaps and \IPS\Settings::i()->google_maps_api_key )
{
foreach ( \IPS\Db::i()->select( 'DISTINCT ip_address', 'core_members_known_ip_addresses', array( 'member_id=?', $this->member->member_id ) ) as $ipAddress )
{
try
{
$location = \IPS\GeoLocation::getByIp( $ipAddress );
$mapMarkers[ $ipAddress ] = array(
'lat' => floatval( $location->lat ),
'long' => floatval( $location->long ),
'title' => $ipAddress
);
}
catch ( \Exception $e ) { }
}
}
return \IPS\Theme::i()->getTemplate('memberprofile')->locations( $this->member, $mapMarkers );
}
}
@@ -0,0 +1,95 @@
<?php
/**
* @brief ACP Member Profile: Login Methods Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Login Methods Block
*/
class _LoginMethods extends \IPS\core\MemberACPProfile\LazyLoadingBlock
{
/**
* Get output
*
* @return string
*/
public function lazyOutput()
{
$loginMethods = array();
foreach ( \IPS\Login::methods() as $method )
{
if ( $method->canProcess( $this->member ) and !( $method instanceof \IPS\Login\Handler\Standard ) )
{
$link = NULL;
try
{
$link = $method->userLink( $method->userId( $this->member ), $method->userProfileName( $this->member ) );
$syncOptions = FALSE;
foreach ( $method->syncOptions( $this->member ) as $option )
{
if ( $option == 'photo' and !$this->member->group['g_edit_profile'] )
{
continue;
}
if ( $option == 'cover' and ( !$this->member->group['g_edit_profile'] or !$this->member->group['gbw_allow_upload_bgimage'] ) )
{
continue;
}
if ( $option == 'status' and ( !$this->member->canAccessModule( \IPS\Application\Module::get( 'core', 'status' ) ) or !\IPS\core\Statuses\Status::canCreateFromCreateMenu( $this->member ) or !\IPS\Settings::i()->profile_comments or $this->member->group['gbw_no_status_update'] ) )
{
continue;
}
$syncOptions = TRUE;
break;
}
$canDisassociate = FALSE;
foreach ( \IPS\Login::methods() as $_method )
{
if ( $_method->id != $method->id and $_method->canProcess( $this->member ) )
{
$canDisassociate = TRUE;
break;
}
}
$loginMethods[ $method->id ] = array(
'title' => $method->_title,
'blurb' => \IPS\Member::loggedIn()->language()->addToStack( 'profilesync_headline', FALSE, array( 'sprintf' => array( $method->userProfileName( $this->member ) ) ) ),
'icon' => $method->userProfilePhoto( $this->member ),
'logo' => $method->logoForUcp(),
'link' => $link,
'edit' => $syncOptions,
'delete'=> $canDisassociate
);
}
catch ( \IPS\Login\Exception $e )
{
$loginMethods[ $method->id ] = array( 'title' => $method->_title, 'blurb' => \IPS\Member::loggedIn()->language()->addToStack('profilesync_reauth_needed'), 'logo' => $method->logoForUcp(), 'link' => $link );
}
catch( \IPS\Http\Request\Exception $e )
{
\IPS\Log::log( $e, 'login_method_connect' );
}
}
}
return \IPS\Theme::i()->getTemplate('memberprofile')->loginMethods( $this->member, $loginMethods );
}
}
@@ -0,0 +1,176 @@
<?php
/**
* @brief ACP Member Profile: MFA Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: MFA Block
*/
class _MFA extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
if ( !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_mfa' ) )
{
return '';
}
$mfaEnabled = FALSE;
$configuredHandlers = array();
$hasSecurityQuestions = FALSE;
$showEditButton = ( \IPS\Settings::i()->mfa_required_groups != '*' and !$this->member->inGroup( explode( ',', \IPS\Settings::i()->mfa_required_groups ) ) );
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $this->member ) )
{
$mfaEnabled = TRUE;
if ( $handler->memberHasConfiguredHandler( $this->member ) )
{
if ( $handler instanceof \IPS\MFA\SecurityQuestions\Handler )
{
$hasSecurityQuestions = TRUE;
}
else
{
$configuredHandlers[ $key ] = $handler;
}
}
if ( !$showEditButton and count( $handler->acpConfiguration( $this->member ) ) )
{
$showEditButton = TRUE;
}
}
}
if ( $mfaEnabled )
{
return \IPS\Theme::i()->getTemplate('memberprofile')->mfa( $this->member, $configuredHandlers, $hasSecurityQuestions, $showEditButton );
}
return '';
}
/**
* Edit Window
*
* @return string
*/
function edit()
{
/* Check permission */
\IPS\Dispatcher::i()->checkAcpPermission( 'member_mfa' );
/* Get all the fields we'll need */
$fields = array();
$optOutToggles = array();
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $this->member ) )
{
foreach ( $handler->acpConfiguration( $this->member ) as $id => $field )
{
$fields[] = $field;
}
$optOutToggles[] = "mfa_{$key}_title";
}
}
/* Build form */
$form = new \IPS\Helpers\Form;
if ( \IPS\Settings::i()->mfa_required_groups != '*' and !$this->member->inGroup( explode( ',', \IPS\Settings::i()->mfa_required_groups ) ) )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'mfa_opt_out_admin', $this->member->members_bitoptions['security_questions_opt_out'], FALSE, array( 'togglesOff' => $optOutToggles ) ) );
}
foreach ( $fields as $id => $field )
{
if ( $field instanceof \IPS\Helpers\Form\Matrix )
{
$form->addMatrix( $field->id, $field );
}
else
{
$form->add( $field );
}
}
/* Handle submissions */
if ( $values = $form->values() )
{
/* Reset the failure count and unlock if necessary */
$this->member->failed_mfa_attempts = 0;
$mfaDetails = $this->member->mfa_details;
if ( isset( $mfaDetails['_lockouttime'] ) )
{
unset( $mfaDetails['_lockouttime'] );
$this->member->mfa_details = $mfaDetails;
}
/* Did we opt out? */
if ( isset( $values['mfa_opt_out_admin'] ) )
{
/* Opt-Out: Disable all handlers */
if ( $values['mfa_opt_out_admin'] )
{
if ( !$this->member->members_bitoptions['security_questions_opt_out'] )
{
$this->member->members_bitoptions['security_questions_opt_out'] = TRUE;
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->memberHasConfiguredHandler( $this->member ) )
{
$handler->disableHandlerForMember( $this->member );
}
}
$this->member->logHistory( 'core', 'mfa', array( 'handler' => 'questions', 'enable' => FALSE, 'optout' => TRUE ) );
$this->member->save();
}
}
/* Opt-In */
elseif ( $this->member->members_bitoptions['security_questions_opt_out'] )
{
$this->member->members_bitoptions['security_questions_opt_out'] = FALSE;
$this->member->save();
$this->member->logHistory( 'core', 'mfa', array( 'handler' => 'questions', 'enable' => FALSE, 'optout' => FALSE ) );
}
}
/* Save each of the handlers */
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $this->member ) )
{
$handler->acpConfigurationSave( $this->member, $values );
}
}
/* Log and Redirect */
\IPS\Session::i()->log( 'acplog__members_edited_mfa', array( $this->member->name => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,55 @@
<?php
/**
* @brief ACP Member Profile: OAuth Apps
* @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 29 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Groups Block
*/
class _OAuth extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'applications', 'oauth_tokens' ) and \IPS\Db::i()->select( 'COUNT(*)', 'core_oauth_clients', array( \IPS\Db::i()->findInSet( 'oauth_grant_types', array( 'authorization_code', 'implicit', 'password' ) ) ) )->first() )
{
$tokens = array();
foreach ( \IPS\Db::i()->select( '*', 'core_oauth_server_access_tokens', array( 'member_id=?', $this->member->member_id ), 'issued DESC' ) as $token )
{
try
{
$client = \IPS\Api\OAuthClient::load( $token['client_id'] );
$tokens[] = array(
'title' => $client->_title,
'use_refresh_tokens' => $client->use_refresh_tokens,
'data' => $token
);
}
catch ( \Exception $e ) {}
}
return \IPS\Theme::i()->getTemplate('memberprofile')->oauth( $this->member, $tokens );
}
}
}
@@ -0,0 +1,211 @@
<?php
/**
* @brief ACP Member Profile: Profile Data Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Profile Data Block
*/
class _ProfileData extends \IPS\core\MemberACPProfile\TabbedBlock
{
/**
* @brief Fields
*/
protected $fields = array();
/**
* @brief Clubs
*/
protected $clubs = NULL;
/**
* Constructor
*
* @param \IPS\Member $member Member
* @return void
*/
public function __construct( \IPS\Member $member )
{
parent::__construct( $member );
$this->fields = $this->member->profileFields( \IPS\core\ProfileFields\Field::STAFF );
$this->clubs = \IPS\Settings::i()->clubs ? \IPS\Member\Club::clubs( NULL, NULL, 'last_activity', array( 'member' => $this->member, 'statuses' => array( \IPS\Member\Club::STATUS_MODERATOR, \IPS\Member\Club::STATUS_EXPIRED_MODERATOR, \IPS\Member\Club::STATUS_LEADER ) ) ) : array();
}
/**
* Get Block Title
*
* @return string
*/
public function blockTitle()
{
return 'profile_data';
}
/**
* Get Tab Names
*
* @return string
*/
public function tabs()
{
$return = array();
if ( count( $this->fields ) || $this->member->member_title || $this->member->rank['title'] || $this->member->rank['image'] || \IPS\Settings::i()->profile_birthday_type != 'none' || \IPS\Settings::i()->signatures_enabled )
{
$return['fields'] = 'profile_fields';
}
if ( count( $this->clubs ) )
{
$return['clubs'] = 'club_ownership';
}
return $return;
}
/**
* Show Edit Link?
*
* @return bool
*/
protected function showEditLink()
{
return true;
}
/**
* Get output
*
* @return string
*/
public function tabOutput( $tab )
{
if ( $tab == 'fields' )
{
return \IPS\Theme::i()->getTemplate('memberprofile')->profileData( $this->member, $this->fields );
}
elseif ( $tab == 'clubs' )
{
return \IPS\Theme::i()->getTemplate('memberprofile')->clubs( $this->member, $this->clubs );
}
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
/* Build basic form */
$form = new \IPS\Helpers\Form;
$form->addHeader('profile_data');
$form->add( new \IPS\Helpers\Form\Text( 'member_title', $this->member->member_title, FALSE, array( 'maxLength' => 64 ) ) );
if ( \IPS\Settings::i()->profile_birthday_type !== 'none' )
{
$form->add( new \IPS\Helpers\Form\Custom( 'bday', array( 'year' => $this->member->bday_year, 'month' => $this->member->bday_month, 'day' => $this->member->bday_day ), FALSE, array( 'getHtml' => function( $element )
{
return strtr( \IPS\Member::loggedIn()->language()->preferredDateFormat(), array(
'DD' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_day( $element->name, $element->value, $element->error ),
'MM' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_month( $element->name, $element->value, $element->error ),
'YY' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_year( $element->name, $element->value, $element->error ),
'YYYY' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_year( $element->name, $element->value, $element->error ),
) );
} ) ) );
}
if ( \IPS\Settings::i()->signatures_enabled )
{
$form->add( new \IPS\Helpers\Form\Editor( 'signature', $this->member->signature, FALSE, array( 'app' => 'core', 'key' => 'Signatures', 'autoSaveKey' => "sig-{$this->member->member_id}", 'attachIds' => array( $this->member->member_id ) ) ) );
}
/* Profile Fields */
try
{
$values = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $this->member->member_id ) )->first();
}
catch( \UnderflowException $e )
{
$values = array();
}
if( count( $values ) )
{
foreach ( \IPS\core\ProfileFields\Field::fields( $values, \IPS\core\ProfileFields\Field::STAFF ) as $group => $fields )
{
$form->addHeader( "core_pfieldgroups_{$group}" );
foreach ( $fields as $field )
{
$form->add( $field );
}
}
}
/* Handle submissions */
if ( $values = $form->values() )
{
/* Profile Fields */
try
{
$profileFields = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $this->member->member_id ) )->first();
if ( !is_array( $profileFields ) ) // If \IPS\Db::i()->select()->first() has only one column, then the contents of that column is returned. We do not want this here
{
$profileFields = array();
}
}
catch( \UnderflowException $e )
{
$profileFields = array();
}
$profileFields['member_id'] = $this->member->member_id;
foreach ( \IPS\core\ProfileFields\Field::fields( $profileFields, \IPS\core\ProfileFields\Field::STAFF ) as $group => $fields )
{
foreach ( $fields as $id => $field )
{
$profileFields[ "field_{$id}" ] = $field::stringValue( !empty( $values[ $field->name ] ) ? $values[ $field->name ] : NULL );
}
}
$this->member->changedCustomFields = $profileFields;
\IPS\Db::i()->replace( 'core_pfields_content', $profileFields );
/* Profile Preferences */
$this->member->member_title = $values['member_title'];
if ( $values['bday'] )
{
$this->member->bday_day = $values['bday']['day'];
$this->member->bday_month = $values['bday']['month'];
$this->member->bday_year = $values['bday']['year'];
}
else
{
$this->member->bday_day = NULL;
$this->member->bday_month = NULL;
$this->member->bday_year = NULL;
}
if ( \IPS\Settings::i()->signatures_enabled )
{
$this->member->signature = $values['signature'];
}
$this->member->save();
/* Log and Redirect */
\IPS\Session::i()->log( 'acplog__members_edited_profile', array( $this->member->name => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,100 @@
<?php
/**
* @brief ACP Member Profile: Quotas Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Quotas Block
*/
class _Quotas extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$messengerCount = NULL;
$messengerPercent = NULL;
if ( $this->member->canAccessModule( \IPS\Application\Module::get( 'core', 'messaging', 'front' ) ) and !$this->member->members_disable_pm )
{
$messengerCount = \IPS\Db::i()->select( 'count(*)', 'core_message_topic_user_map', array( 'map_user_id=? AND map_user_active=1', $this->member->member_id ) )->first();
if ( $this->member->group['g_max_messages'] > 0 )
{
$messengerPercent = floor( 100 / $this->member->group['g_max_messages'] * $messengerCount );;
}
}
$attachmentStorage = NULL;
$attachmentPercent = NULL;
if ( $this->member->group['g_attach_max'] != 0 )
{
$attachmentStorage = \IPS\Db::i()->select( 'SUM(attach_filesize)', 'core_attachments', array( 'attach_member_id=?', $this->member->member_id ) )->first();
if ( !$attachmentStorage )
{
$attachmentStorage = 0;
}
if ( $this->member->group['g_attach_max'] > 0 )
{
$attachmentPercent = floor( 100 / ( $this->member->group['g_attach_max'] * 1024 ) * $attachmentStorage );
}
}
$viewAttachmentsLink = NULL;
if ( $attachmentStorage and \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'overview', 'files_view' ) )
{
$viewAttachmentsLink = \IPS\Http\Url::internal('app=core&module=overview&controller=files&advanced_search_submitted=1')->setQueryString( 'attach_member_id', $this->member->name )->csrf();
}
return \IPS\Theme::i()->getTemplate('memberprofile')->quotas( $this->member, $messengerCount, $messengerPercent, $attachmentStorage, $attachmentPercent, $viewAttachmentsLink );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
$old = $this->member->members_disable_pm;
if ( \IPS\Request::i()->enable )
{
$this->member->members_disable_pm = 0;
}
else
{
if ( \IPS\Request::i()->prompt ) // Member cannot re-enable
{
$this->member->members_disable_pm = 2;
}
else // Member can re-enable
{
$this->member->members_disable_pm = 1;
}
}
$this->member->save();
if ( $old != $this->member->members_disable_pm )
{
$this->member->logHistory( 'core', 'warning', array( 'restrictions' => array( 'members_disable_pm' => array( 'old' => $old, 'new' => $this->member->members_disable_pm ) ) ) );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
}
@@ -0,0 +1,149 @@
<?php
/**
* @brief ACP Member Profile: Warnings Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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;
}
/**
* @brief ACP Member Profile: Warnings Block
*/
class _Warnings extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$restrictions = array();
foreach ( \IPS\Application::allExtensions( 'core', 'MemberRestrictions', TRUE, 'core', 'Content', FALSE ) as $class )
{
try
{
$ext = new $class( $this->member );
if ( $ext->enabled() )
{
foreach ( $ext->activeRestrictions() as $v )
{
$restrictions[] = $v;
}
}
}
catch ( \Exception $e ) { }
}
$flagActions = array();
$spamOption = explode( ',', \IPS\Settings::i()->spm_option );
if ( in_array( 'delete', $spamOption ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_delete');
}
elseif ( in_array( 'unapprove', $spamOption ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_unapprove');
}
if ( in_array( 'ban', $spamOption ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_ban');
}
elseif ( in_array( 'disable', $spamOption ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_disable');
}
if ( \IPS\Settings::i()->spam_service_enabled and \IPS\Settings::i()->spam_service_send_to_ips )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_report');
}
if ( count( $flagActions ) )
{
$flagMessage = sprintf( \IPS\Member::loggedIn()->language()->get('spam_flag_confirm'), \IPS\Member::loggedIn()->language()->formatList( $flagActions ) );
}
return \IPS\Theme::i()->getTemplate('memberprofile')->warnings( $this->member, $restrictions, $flagMessage );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
/* Get extensions */
$extensions = array();
foreach ( \IPS\Application::allExtensions( 'core', 'MemberRestrictions', TRUE, 'core', 'Content', FALSE ) as $class )
{
try
{
$ext = new $class( $this->member );
if ( $ext->enabled() )
{
$exploded = explode( '\\', $class );
$extensions[ $exploded[1] . '_' . $exploded[5] ] = $ext;
}
}
catch ( \Exception $e ) { }
}
/* Build Form */
$form = new \IPS\Helpers\Form;
if ( \IPS\Settings::i()->warn_on )
{
$form->addHeader( 'warnings' );
$form->add( new \IPS\Helpers\Form\Number( 'member_warnings', $this->member->warn_level, FALSE ) );
}
foreach ( $extensions as $key => $ext )
{
$form->addHeader( 'member_restrictions__' . $key );
$ext->form( $form );
}
/* Handle Submissions */
if ( $values = $form->values() )
{
$changes = array();
if ( \IPS\Settings::i()->warn_on )
{
if ( $this->member->warn_level != $values['member_warnings'] )
{
$changes['member_warnings'] = array( 'old' => $this->member->warn_level, 'new' => $values['member_warnings'] );
$this->member->warn_level = $values['member_warnings'];
}
}
foreach ( $extensions as $key => $ext )
{
foreach ( $ext->save( $values ) as $k => $v )
{
$changes[ $k ] = $v;
}
}
if ( count( $changes ) )
{
$this->member->logHistory( 'core', 'warning', array( 'restrictions' => $changes ) );
}
$this->member->save();
\IPS\Session::i()->log( 'acplog__members_edited_restrictions', array( $this->member->name => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
Whitespace-only changes.
@@ -0,0 +1,56 @@
<?php
/**
* @brief ACP Member Profile: Main Tab
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileTabs;
/* 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;
}
/**
* @brief ACP Member Profile: Main Tab
*/
class _Main extends \IPS\core\MemberACPProfile\MainTab
{
/**
* Get left-column blocks
*
* @return array
*/
public function leftColumnBlocks()
{
return array(
'IPS\core\extensions\core\MemberACPProfileBlocks\BasicInformation',
'IPS\core\extensions\core\MemberACPProfileBlocks\Groups',
'IPS\core\extensions\core\MemberACPProfileBlocks\DevicesAndIPAddresses',
'IPS\core\extensions\core\MemberACPProfileBlocks\MFA',
'IPS\core\extensions\core\MemberACPProfileBlocks\OAuth'
);
}
/**
* Get main-column blocks
*
* @return array
*/
public function mainColumnBlocks()
{
return array(
'IPS\core\extensions\core\MemberACPProfileBlocks\Header',
'IPS\core\extensions\core\MemberACPProfileBlocks\ContentStatistics',
'IPS\core\extensions\core\MemberACPProfileBlocks\Quotas',
'IPS\core\extensions\core\MemberACPProfileBlocks\Warnings',
'IPS\core\extensions\core\MemberACPProfileBlocks\ProfileData',
);
}
}
Whitespace-only changes.
@@ -30,7 +30,7 @@ class _Content
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -112,9 +112,10 @@ class _Content
* @note This is only necessary if availableIn() includes group_promotions
* @param \IPS\Member $member Member object to check
* @param array $filters Previously defined filters
* @param object|NULL $object Calling class
* @return bool
*/
public function matches( \IPS\Member $member, $filters )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( !isset( $filters['content_count_operator'] ) OR !isset( $filters['content_count_score'] ) )
@@ -138,4 +139,31 @@ class _Content
/* If we are still here, then there wasn't an appropriate operator (maybe they selected 'any') so return true */
return TRUE;
}
/**
* Return a lovely human description for this rule if used
*
* @param mixed $filters The array returned from the save() method
* @return string|NULL
*/
public function getDescription( $filters )
{
if ( ! empty( $filters['content_count_score'] ) and $filters['content_count_score'] > 0 )
{
switch ( $filters['content_count_operator'] )
{
case 'gt':
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_gt_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
break;
case 'lt':
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_lt_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
break;
case 'eq':
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_eq_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
break;
}
}
return NULL;
}
}
@@ -30,7 +30,7 @@ class _Group
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -70,6 +70,41 @@ class _Group
);
}
/**
* Return a lovely human description for this rule if used
*
* @param mixed $data The array returned from the save() method
* @return string|NULL
*/
public function getDescription( $data )
{
if ( $data['groups'] )
{
$_groups = explode( ',', $data['groups'] );
$humanGroups = array();
foreach( $_groups as $gid )
{
/* Uses datastore so not as bad as it looks mkay */
try
{
$humanGroups[] = \IPS\Member\Group::load( $gid )->name;
}
catch( \Exception $e )
{
continue;
}
}
if ( count( $humanGroups ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_group_desc', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $humanGroups ) ) ) );
}
}
return NULL;
}
/**
* Save the filter data
*
@@ -115,9 +150,10 @@ class _Group
* @note This is only necessary if availableIn() includes group_promotions
* @param \IPS\Member $member Member object to check
* @param array $filters Previously defined filters
* @param object|NULL $object Calling class
* @return bool
*/
public function matches( \IPS\Member $member, $filters )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( !isset( $filters['groups'] ) OR !$filters['groups'] )
@@ -127,7 +163,15 @@ class _Group
$_groups = explode( ',', $filters['groups'] );
/* This intentionally checks secondary groups, as SQL filtering also checks secondary groups */
return (bool) count( array_intersect( $_groups, $member->groups ) );
/* If no object is passed, or the property is not set, then we will check secondary groups */
if( $object === NULL OR !isset( $object->memberFilterCheckSecondaryGroups ) OR $object->memberFilterCheckSecondaryGroups === TRUE )
{
/* This checks secondary groups */
return (bool) count( array_intersect( $_groups, $member->groups ) );
}
else
{
return in_array( $member->member_group_id, $_groups );
}
}
}
@@ -30,7 +30,7 @@ class _Joined
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -42,7 +42,7 @@ class _Joined
public function getSettingField( $criteria )
{
return array(
new \IPS\Helpers\Form\Custom( 'bmf_members_joined', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL ), FALSE, array(
new \IPS\Helpers\Form\Custom( 'bmf_members_joined', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL, 3 => isset( $criteria['days_lt'] ) ? $criteria['days_lt'] : NULL ), FALSE, array(
'getHtml' => function( $element )
{
$dateRange = new \IPS\Helpers\Form\DateRange( "{$element->name}[0]", $element->value[0], FALSE );
@@ -62,9 +62,16 @@ class _Joined
*/
public function save( $post )
{
if( isset( $post['bmf_members_joined'][2] ) AND $post['bmf_members_joined'][2] == 'days' )
if ( isset( $post['bmf_members_joined'][2] ) )
{
return $post['bmf_members_joined'][1] ? array( 'days' => intval( $post['bmf_members_joined'][1] ) ) : FALSE;
if ( $post['bmf_members_joined'][2] == 'days' )
{
return $post['bmf_members_joined'][1] ? array( 'days' => intval( $post['bmf_members_joined'][1] ) ) : FALSE;
}
else if ( $post['bmf_members_joined'][2] == 'days_lt' )
{
return $post['bmf_members_joined'][3] ? array( 'days_lt' => intval( $post['bmf_members_joined'][3] ) ) : FALSE;
}
}
else
{
@@ -83,20 +90,26 @@ class _Joined
{
if( !empty($data['range']) AND !empty($data['range']['end']) )
{
$start = ( $data['range']['start']['date'] ) ? new \IPS\DateTime( $data['range']['start']['date'] ) : NULL;
$end = ( $data['range']['end']['date'] ) ? new \IPS\DateTime( $data['range']['end']['date'] ) : NULL;
$start = ( $data['range']['start'] ) ? new \IPS\DateTime( $data['range']['start'] ) : NULL;
$end = ( $data['range']['end'] ) ? new \IPS\DateTime( $data['range']['end'] ) : NULL;
if( $start and $end )
{
return "core_members.joined BETWEEN {$start->getTimestamp()} AND {$end->getTimestamp()}";
}
}
elseif( !empty($data['days']) )
elseif( !empty( $data['days'] ) AND (int) $data['days'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $data['days'] . 'D' ) );
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days'] . 'D' ) );
return "core_members.joined < {$date->getTimestamp()}";
}
elseif( !empty( $data['days_lt'] ) AND (int) $data['days_lt'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days_lt'] . 'D' ) );
return "core_members.joined > {$date->getTimestamp()}";
}
return NULL;
}
@@ -107,9 +120,10 @@ class _Joined
* @note This is only necessary if availableIn() includes group_promotions
* @param \IPS\Member $member Member object to check
* @param array $filters Previously defined filters
* @param object|NULL $object Calling class
* @return bool
*/
public function matches( \IPS\Member $member, $filters )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( ( !isset( $filters['range'] ) OR !$filters['range'] OR empty( $filters['range']['end'] ) ) AND ( !isset( $filters['days'] ) OR !$filters['days'] ) )
@@ -130,9 +144,39 @@ class _Joined
return (bool) ( $joinedDate->getTimestamp() > $start->getTimestamp() AND $joinedDate->getTimestamp() < $end->getTimestamp() );
}
}
elseif( !empty( $filters['days'] ) )
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return (bool) ( $joinedDate->add( new \DateInterval( 'P' . $filters['days'] . 'D' ) )->getTimestamp() < time() );
return (bool) ( $joinedDate->add( new \DateInterval( 'P' . (int) $filters['days'] . 'D' ) )->getTimestamp() < time() );
}
}
/**
* Return a lovely human description for this rule if used
*
* @param mixed $filters The array returned from the save() method
* @return string|NULL
*/
public function getDescription( $filters )
{
if( !empty( $filters['range'] ) AND !empty( $filters['range']['end'] ) )
{
$start = ( $filters['range']['start'] ) ? new \IPS\DateTime( $filters['range']['start'] ) : NULL;
$end = ( $filters['range']['end'] ) ? new \IPS\DateTime( $filters['range']['end'] ) : NULL;
if ( $start and $end )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
}
}
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) );
}
elseif( !empty( $filters['days_lt'] ) AND (int) $filters['days_lt'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) );
}
return NULL;
}
}
@@ -30,7 +30,7 @@ class _Lastpost
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -42,7 +42,7 @@ class _Lastpost
public function getSettingField( $criteria )
{
return array(
new \IPS\Helpers\Form\Custom( 'mf_members_last_post', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL ), FALSE, array(
new \IPS\Helpers\Form\Custom( 'mf_members_last_post', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL, 3 => isset( $criteria['days_lt'] ) ? $criteria['days_lt'] : NULL ), FALSE, array(
'getHtml' => function( $element )
{
$dateRange = new \IPS\Helpers\Form\DateRange( "{$element->name}[0]", $element->value[0], FALSE );
@@ -62,9 +62,16 @@ class _Lastpost
*/
public function save( $post )
{
if( isset( $post['mf_members_last_post'][2] ) AND $post['mf_members_last_post'][2] == 'days' )
if ( isset( $post['mf_members_last_post'][2] ) )
{
return $post['mf_members_last_post'][1] ? array( 'days' => intval( $post['mf_members_last_post'][1] ) ) : FALSE;
if ( $post['mf_members_last_post'][2] == 'days' )
{
return $post['mf_members_last_post'][1] ? array( 'days' => intval( $post['mf_members_last_post'][1] ) ) : FALSE;
}
else if ( $post['mf_members_last_post'][2] == 'days_lt' )
{
return $post['mf_members_last_post'][3] ? array( 'days_lt' => intval( $post['mf_members_last_post'][3] ) ) : FALSE;
}
}
else
{
@@ -83,20 +90,27 @@ class _Lastpost
{
if( !empty($data['range']) AND !empty($data['range']['end']) )
{
$start = ( $data['range']['start']['date'] ) ? new \IPS\DateTime( $data['range']['start']['date'] ) : NULL;
$end = ( $data['range']['end']['date'] ) ? new \IPS\DateTime( $data['range']['end']['date'] ) : NULL;
$start = ( $data['range']['start'] ) ? new \IPS\DateTime( $data['range']['start'] ) : NULL;
$end = ( $data['range']['end'] ) ? new \IPS\DateTime( $data['range']['end'] ) : NULL;
if( $start and $end )
{
return "core_members.member_last_post BETWEEN {$start} AND {$end}";
return "core_members.member_last_post BETWEEN {$start->getTimestamp()} AND {$end->getTimestamp()}";
}
}
elseif( !empty($data['days']) )
elseif( !empty( $data['days'] ) AND (int) $data['days'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $data['days'] . 'D' ) );
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days'] . 'D' ) );
return "core_members.member_last_post < {$date->getTimestamp()}";
return "( core_members.member_last_post < {$date->getTimestamp()} OR core_members.member_last_post IS NULL )";
}
elseif( !empty( $data['days_lt'] ) AND (int) $data['days_lt'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days_lt'] . 'D' ) );
return "( core_members.member_last_post > {$date->getTimestamp()} )";
}
return NULL;
}
@@ -107,9 +121,10 @@ class _Lastpost
* @note This is only necessary if availableIn() includes group_promotions
* @param \IPS\Member $member Member object to check
* @param array $filters Previously defined filters
* @param object|NULL $object Calling class
* @return bool
*/
public function matches( \IPS\Member $member, $filters )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( ( !isset( $filters['range'] ) OR !$filters['range'] OR empty( $filters['range']['end'] ) ) AND ( !isset( $filters['days'] ) OR !$filters['days'] ) )
@@ -117,6 +132,20 @@ class _Lastpost
return TRUE;
}
/* If the member hasn't posted anything yet, he has NULL as member_last_post */
if ( !$member->member_last_post )
{
/* But if we want members who haven't posted in more than X days, return anyone who hasn't posted at all as well */
if( !empty( $filters['days'] ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
$lastPost = \IPS\DateTime::ts( $member->member_last_post );
if( !empty( $filters['range'] ) AND !empty( $filters['range']['end'] ) )
@@ -129,9 +158,39 @@ class _Lastpost
return (bool) ( $lastPost->getTimestamp() > $start->getTimestamp() AND $lastPost->getTimestamp() < $end->getTimestamp() );
}
}
elseif( !empty( $filters['days'] ) )
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return (bool) ( $lastPost->add( new \DateInterval( 'P' . $filters['days'] . 'D' ) )->getTimestamp() < time() );
return (bool) ( $lastPost->add( new \DateInterval( 'P' . (int) $filters['days'] . 'D' ) )->getTimestamp() < time() );
}
}
/**
* Return a lovely human description for this rule if used
*
* @param mixed $filters The array returned from the save() method
* @return string|NULL
*/
public function getDescription( $filters )
{
if( !empty( $filters['range'] ) AND !empty( $filters['range']['end'] ) )
{
$start = ( $filters['range']['start'] ) ? new \IPS\DateTime( $filters['range']['start'] ) : NULL;
$end = ( $filters['range']['end'] ) ? new \IPS\DateTime( $filters['range']['end'] ) : NULL;
if ( $start and $end )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
}
}
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) );
}
elseif( !empty( $filters['days_lt'] ) AND (int) $filters['days_lt'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) );
}
return NULL;
}
}
@@ -30,7 +30,7 @@ class _Lastvisit
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -42,7 +42,7 @@ class _Lastvisit
public function getSettingField( $criteria )
{
return array(
new \IPS\Helpers\Form\Custom( 'bmf_members_last_visit', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL ), FALSE, array(
new \IPS\Helpers\Form\Custom( 'bmf_members_last_visit', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL, 3 => isset( $criteria['days_lt'] ) ? $criteria['days_lt'] : NULL ), FALSE, array(
'getHtml' => function( $element )
{
$dateRange = new \IPS\Helpers\Form\DateRange( "{$element->name}[0]", $element->value[0], FALSE );
@@ -62,9 +62,16 @@ class _Lastvisit
*/
public function save( $post )
{
if( isset( $post['bmf_members_last_visit'][2] ) AND $post['bmf_members_last_visit'][2] == 'days' )
if ( isset( $post['bmf_members_last_visit'][2] ) )
{
return $post['bmf_members_last_visit'][1] ? array( 'days' => intval( $post['bmf_members_last_visit'][1] ) ) : FALSE;
if ( $post['bmf_members_last_visit'][2] == 'days' )
{
return $post['bmf_members_last_visit'][1] ? array( 'days' => intval( $post['bmf_members_last_visit'][1] ) ) : FALSE;
}
else if ( $post['bmf_members_last_visit'][2] == 'days_lt' )
{
return $post['bmf_members_last_visit'][3] ? array( 'days_lt' => intval( $post['bmf_members_last_visit'][3] ) ) : FALSE;
}
}
else
{
@@ -91,12 +98,18 @@ class _Lastvisit
return "core_members.last_visit BETWEEN {$start->getTimestamp()} AND {$end->getTimestamp()}";
}
}
elseif( !empty($data['days']) )
elseif( !empty( $data['days'] ) AND (int) $data['days'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $data['days'] . 'D' ) );
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days'] . 'D' ) );
return "core_members.last_visit < {$date->getTimestamp()}";
}
elseif( !empty( $data['days_lt'] ) AND (int) $data['days_lt'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days_lt'] . 'D' ) );
return "core_members.last_visit > {$date->getTimestamp()}";
}
return NULL;
}
@@ -107,9 +120,10 @@ class _Lastvisit
* @note This is only necessary if availableIn() includes group_promotions
* @param \IPS\Member $member Member object to check
* @param array $filters Previously defined filters
* @param object|NULL $object Calling class
* @return bool
*/
public function matches( \IPS\Member $member, $filters )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( ( !isset( $filters['range'] ) OR !$filters['range'] OR empty( $filters['range']['end'] ) ) AND ( !isset( $filters['days'] ) OR !$filters['days'] ) )
@@ -129,9 +143,39 @@ class _Lastvisit
return (bool) ( $lastVisit->getTimestamp() > $start->getTimestamp() AND $lastVisit->getTimestamp() < $end->getTimestamp() );
}
}
elseif( !empty( $filters['days'] ) )
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return (bool) ( $lastVisit->add( new \DateInterval( 'P' . $filters['days'] . 'D' ) )->getTimestamp() < time() );
return (bool) ( $lastVisit->add( new \DateInterval( 'P' . (int) $filters['days'] . 'D' ) )->getTimestamp() < time() );
}
}
/**
* Return a lovely human description for this rule if used
*
* @param mixed $filters The array returned from the save() method
* @return string|NULL
*/
public function getDescription( $filters )
{
if( !empty( $filters['range'] ) AND !empty( $filters['range']['end'] ) )
{
$start = ( $filters['range']['start'] ) ? new \IPS\DateTime( $filters['range']['start'] ) : NULL;
$end = ( $filters['range']['end'] ) ? new \IPS\DateTime( $filters['range']['end'] ) : NULL;
if ( $start and $end )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
}
}
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) );
}
elseif( !empty( $filters['days_lt'] ) AND (int) $filters['days_lt'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) );
}
return NULL;
}
}
@@ -83,9 +83,10 @@ class _Leaderboard
* @note This is only necessary if availableIn() includes group_promotions
* @param \IPS\Member $member Member object to check
* @param array $filters Previously defined filters
* @param object|NULL $object Calling class
* @return bool
*/
public function matches( \IPS\Member $member, $filters )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( !isset( $filters['leaderboard'] ) OR !$filters['leaderboard'] )
@@ -1,114 +1,4 @@
<?php
/**
* @brief Member filter extension: Linked accounts
* @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 22 Mar 2017
*/
namespace IPS\core\extensions\core\MemberFilter;
// Removed
/* 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;
}
/**
* @brief Member filter: Member last visit date
*/
class _Linkedaccounts
{
/**
* Determine if the filter is available in a given area
*
* @param string $area Area to check
* @return bool
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail' ) );
}
/**
* Get Setting Field
*
* @param mixed $criteria Value returned from the save() method
* @return array Array of form elements
*/
public function getSettingField( $criteria )
{
$services = array();
$options = array( 'any' => 'any', 'linked' => 'mf_profile_linked', 'unlinked' => 'mf_profile_unlinked' );
foreach ( \IPS\core\ProfileSync\ProfileSyncAbstract::services() as $key => $class )
{
if( $class::memberFilterLinkedWhere() )
{
$services[] = new \IPS\Helpers\Form\Radio( 'profilesync__' . $key, isset( $criteria['profilesync__' . $key] ) ? $criteria['profilesync__' . $key] : 'any', FALSE, array( 'options' => $options ) );
}
}
return $services;
}
/**
* Save the filter data
*
* @param array $post Form values
* @return mixed False, or an array of data to use later when filtering the members
* @throws \LogicException
*/
public function save( $post )
{
$return = array();
foreach ( \IPS\core\ProfileSync\ProfileSyncAbstract::services() as $key => $class )
{
if( isset( $post['profilesync__' . $key] ) and in_array( $post['profilesync__' . $key], array( 'linked', 'unlinked' ) ) )
{
$return['profilesync__' . $key] = $post['profilesync__' . $key];
}
}
return count( $return ) ? $return : FALSE;
}
/**
* Get where clause to add to the member retrieval database query
*
* @param mixed $data The array returned from the save() method
* @return string|array|NULL Where clause
*/
public function getQueryWhereClause( $data )
{
$return = array();
foreach ( \IPS\core\ProfileSync\ProfileSyncAbstract::services() as $key => $class )
{
if( !isset( $data['profilesync__' . $key] ) )
{
continue;
}
switch( $data['profilesync__' . $key] )
{
case 'linked':
$where = $class::memberFilterLinkedWhere();
break;
case 'unlinked':
$where = $class::memberFilterLinkedWhere( TRUE );
break;
default:
break;
}
if( $where )
{
$return[] = $where;
}
}
return $return;
}
}
@@ -30,7 +30,7 @@ class _Reputation
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -112,9 +112,10 @@ class _Reputation
* @note This is only necessary if availableIn() includes group_promotions
* @param \IPS\Member $member Member object to check
* @param array $filters Previously defined filters
* @param object|NULL $object Calling class
* @return bool
*/
public function matches( \IPS\Member $member, $filters )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( !isset( $filters['reputation_operator'] ) OR !$filters['reputation_operator'] OR !isset( $filters['reputation_score'] ) OR !$filters['reputation_score'] )
@@ -138,4 +139,31 @@ class _Reputation
/* If we are still here, then there wasn't an appropriate operator (maybe they selected 'any') so return true */
return TRUE;
}
/**
* Return a lovely human description for this rule if used
*
* @param mixed $filters The array returned from the save() method
* @return string|NULL
*/
public function getDescription( $filters )
{
if ( ! empty( $filters['reputation_score'] ) and $filters['reputation_score'] > 0 )
{
switch ( $filters['reputation_operator'] )
{
case 'gt':
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_gt_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
break;
case 'lt':
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_lt_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
break;
case 'eq':
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_eq_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
break;
}
}
return NULL;
}
}
@@ -41,7 +41,6 @@ class _Twofactor
*/
public function getSettingField( $criteria )
{
$options = array( 'any' => 'any', 'active' => 'mf_two_factor_active', 'inactive' => 'mf_two_factor_inactive' );
return array(
@@ -8,250 +8,4 @@
* @since 08 Apr 2013
*/
namespace IPS\core\extensions\core\MemberForm;
/* 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;
}
/**
* Admin CP Member Form
*/
class _BasicInformation
{
/**
* Action Buttons
*
* @param \IPS\Member $member The Member
* @return array
*/
public function actionButtons( $member )
{
$return = array();
/* If we can sign in as, flag as spammer, merge, ban or delete content */
if ( ( \IPS\Member::loggedIn()->member_id != $member->member_id AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_login' ) AND !$member->isBanned() ) || ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit_admin' ) or !$member->isAdmin() ) AND $member->member_id != \IPS\Member::loggedIn()->member_id ) || \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'members_merge' ) || ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_ban' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_ban_admin' ) or !$member->isAdmin() ) AND $member->member_id != \IPS\Member::loggedIn()->member_id ) || ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'membertools_delete' ) ) )
{
$return['actions'] = array(
'title' => 'member_account_actions',
'icon' => 'user',
'primary' => TRUE,
'link' => '#',
'menu' => array()
);
if ( \IPS\Member::loggedIn()->member_id != $member->member_id AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_login' ) AND !$member->isBanned() )
{
$return['actions']['menu'][] = array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'login_as_x', FALSE, array( 'sprintf' => array( $member->name ) ) ),
'icon' => 'key',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=login&id={$member->member_id}" ),
'class' => '',
'target' => '_blank'
);
}
/* Flag */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit_admin' ) or !$member->isAdmin() ) AND $member->member_id != \IPS\Member::loggedIn()->member_id )
{
$return['actions']['menu'][] = array(
'title' => $member->members_bitoptions['bw_is_spammer'] ? 'spam_unflag' : 'spam_flag',
'icon' => 'flag',
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=spam&id=' )->setQueryString( array( 'id' => $member->member_id, 'status' => $member->members_bitoptions['bw_is_spammer'] ? 0 : 1 ) ),
'class' => ''
);
}
/* Merge */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'members_merge' ) )
{
$return['actions']['menu'][] = array(
'title' => 'merge',
'icon' => 'level-up',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=merge&id={$member->member_id}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('merge') )
);
}
/* Ban */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_ban' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_ban_admin' ) or !$member->isAdmin() ) AND $member->member_id != \IPS\Member::loggedIn()->member_id )
{
$return['actions']['menu'][] = array(
'title' => $member->temp_ban ? 'adjust_ban' : 'ban',
'icon' => 'times',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=ban&id={$member->member_id}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => $member->temp_ban ? \IPS\Member::loggedIn()->language()->addToStack('adjust_ban') : \IPS\Member::loggedIn()->language()->addToStack('ban') )
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'membertools_delete' ) )
{
$return['actions']['menu'][] = array(
'title' => 'member_delete_content',
'icon' => 'trash-o',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=deleteContent&id={$member->member_id}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('member_delete_content') )
);
}
}
$return['view'] = array(
'title' => 'profile_view_profile',
'icon' => 'search',
'link' => $member->url(),
'class' => '',
'target' => '_blank'
);
if ( \IPS\Settings::i()->warn_on AND \IPS\Member::loggedIn()->modPermission('mod_see_warn') )
{
$return['warnings'] = array(
'title' => 'modcp_view_warnings',
'icon' => 'exclamation-triangle',
'link' => \IPS\Http\Url::internal( "app=core&module=system&controller=warnings&id=" . $member->member_id, 'front', 'warn_list', array( $member->members_seo_name ) ),
'target' => '_blank'
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_history' ) )
{
$return['history'] = array(
'title' => 'member_history',
'icon' => 'history',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=history&id=" . $member->member_id )
);
}
return $return;
}
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
/* Username */
$form->addHeader('member_basic_information');
$form->add( new \IPS\Helpers\Form\Text( 'ips_member_name', $member->name, TRUE, array( 'accountUsername' => $member ), NULL, NULL, '<a data-ipsDialog data-ipsDialog-title="' . \IPS\Member::loggedIn()->language()->addToStack( 'dname_history' ) . '" data-ipsDialog-url="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=viewDnameHistory&id={$member->member_id}" ) . '" href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=viewDnameHistory&id={$member->member_id}" ) . '">' . \IPS\Member::loggedIn()->language()->addToStack( 'view_username_history' ) . '</a>' ) );
/* Password */
$form->add( new \IPS\Helpers\Form\Custom( 'password', NULL, FALSE, array(
'getHtml' => function( $element ) use ( $member )
{
return \IPS\Theme::i()->getTemplate('members')->changePassword( $element->name, $member->member_id );
}
) ) );
/* Email */
$form->add( new \IPS\Helpers\Form\Email( 'ips_address_mail', $member->email, TRUE, array( 'accountEmail' => $member ) ) );
/* Group - if this member is an admin, we need the "Can move admins into other groups" restriction */
if ( !$member->isAdmin() or \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin1' ) )
{
/* If we are editing ourselves, we can only move ourselves into a group with the same restrictions as what we have now... */
if ( $member->member_id == \IPS\Member::loggedIn()->member_id )
{
/* Get the row... */
try
{
$currentRestrictions = \IPS\Db::i()->select( 'row_perm_cache', 'core_admin_permission_rows', array( 'row_id=? AND row_id_type=?', $member->member_group_id, 'group' ) )->first();
$availableGroups = array();
foreach( \IPS\Db::i()->select( 'row_id', 'core_admin_permission_rows', array( 'row_perm_cache=? AND row_id_type=?', $currentRestrictions, 'group' ) ) AS $groupId )
{
$availableGroups[ $groupId ] = \IPS\Member\Group::load( $groupId );
}
}
/* If we don't have a row in core_admin_permission_rows, we're an admin as a member rather than apart of our group, so we can be moved anywhere and it won't matter because member-level restrictions override group-level */
catch ( \UnderflowException $e )
{
$availableGroups = \IPS\Member\Group::groups( TRUE, FALSE );
}
}
/* Not editing ourselves - do we have the Can move members into admin groups"" restriction? */
else
{
$availableGroups = \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE );
}
$form->add( new \IPS\Helpers\Form\Select( 'group', $member->member_group_id, TRUE, array( 'options' => $availableGroups, 'parse' => 'normal' ) ) );
$form->add( new \IPS\Helpers\Form\Select( 'secondary_groups', array_filter( explode( ',', $member->mgroup_others ) ), FALSE, array( 'options' => \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE ), 'multiple' => TRUE, 'parse' => 'normal' ) ) );
}
/* Counts */
$confirmButtons = json_encode( array(
'yes' => \IPS\Member::loggedIn()->language()->addToStack('yes'),
'no' => \IPS\Member::loggedIn()->language()->addToStack('recount_all'),
'cancel' => \IPS\Member::loggedIn()->language()->addToStack('cancel'),
) );
$form->addHeader('member_counts');
$form->add( new \IPS\Helpers\Form\Number( 'member_content_items', $member->member_posts, FALSE, array(), NULL, NULL, '<a data-confirm data-confirmType="verify" data-confirmButtons=\'' . $confirmButtons . '\' data-confirmSubMessage="' . \IPS\Member::loggedIn()->language()->addToStack('member_content_items_recount') . '" href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=recountContent&id={$member->member_id}") . '">' . \IPS\Member::loggedIn()->language()->addToStack('recount') . '</a>' ) );
if ( \IPS\Settings::i()->reputation_enabled )
{
$form->add( new \IPS\Helpers\Form\Number( 'member_reputation', $member->pp_reputation_points, FALSE, array( 'min' => NULL ), NULL, NULL, '<a data-confirm data-confirmType="verify" data-confirmButtons=\'' . $confirmButtons . '\' data-confirmSubMessage="' . \IPS\Member::loggedIn()->language()->addToStack('member_reputation_recount') . '" href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=recountReputation&id={$member->member_id}") . '">' . \IPS\Member::loggedIn()->language()->addToStack('recount') . '</a>' . ' &middot; <a data-confirm href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=removeReputation&id={$member->member_id}&type=given") . '">' . \IPS\Member::loggedIn()->language()->addToStack('reputation_remove_given') . '</a>' . ' &middot; <a data-confirm href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=removeReputation&id={$member->member_id}&type=received") . '">' . \IPS\Member::loggedIn()->language()->addToStack('reputation_remove_received') . '</a>' ) );
}
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
if ( $values['ips_member_name'] != $member->name )
{
foreach ( \IPS\Login::handlers( TRUE ) as $handler )
{
try
{
$handler->changeUsername( $member, $member->name, $values['ips_member_name'] );
}
catch( \BadMethodCallException $e ) {}
}
}
if ( $values['ips_address_mail'] != $member->email )
{
$oldEmail = $member->email;
foreach ( \IPS\Login::handlers( TRUE ) as $handler )
{
try
{
$handler->changeEmail( $member, $oldEmail, $values['ips_address_mail'] );
}
catch( \BadMethodCallException $e ) {}
}
$member->invalidateSessionsAndLogins( TRUE, \IPS\Session::i()->id );
}
if ( !$member->isAdmin() or \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin1' ) )
{
$member->member_group_id = $values['group'];
$member->mgroup_others = implode( ',', $values['secondary_groups'] );
}
$member->member_posts = $values['member_content_items'];
if ( \IPS\Settings::i()->reputation_enabled )
{
$member->pp_reputation_points = $values['member_reputation'];
}
if ( $values['password'] )
{
$member->members_pass_salt = $member->generateSalt();
$member->members_pass_hash = $member->encryptedPassword( $values['password'] );
$member->invalidateSessionsAndLogins( TRUE, \IPS\Session::i()->id );
}
/* Reset Profile Complete flag in case this was an optional step */
$member->members_bitoptions['profile_completed'] = FALSE;
}
}
// Removed
@@ -64,7 +64,12 @@ class _Notifications
*/
public function save( $values, &$member )
{
if ( $member->allow_admin_mails != $values['allow_admin_mails'] )
{
$member->logHistory( 'core', 'admin_mails', array( 'enabled' => (bool) $values['allow_admin_mails'] ) );
}
$member->allow_admin_mails = (int) $values['allow_admin_mails'];
$member->auto_track = json_encode( array(
'content' => ( is_array( $values['auto_track'] ) AND in_array( 'content', $values['auto_track'] ) ) ? 1 : 0,
'comments' => ( is_array( $values['auto_track'] ) AND in_array( 'comments', $values['auto_track'] ) ) ? 1 : 0,
@@ -35,7 +35,7 @@ class _Preferences
$form->addHeader('member_preferences_system');
$form->add( new \IPS\Helpers\Form\YesNo( 'member_timezone_override',!$member->members_bitoptions['timezone_override'], FALSE, array( 'togglesOff' => array( 'timezone' ) ) ) );
$timezones = array();
foreach ( \DateTimeZone::listIdentifiers() as $tz )
foreach ( \IPS\DateTime::getTimezoneIdentifiers() as $tz )
{
if ( $pos = mb_strpos( $tz, '/' ) )
{
@@ -78,6 +78,11 @@ class _Preferences
/* Content */
$form->addHeader('member_preferences_content');
$form->add( new \IPS\Helpers\Form\YesNo( 'view_sigs', $member->members_bitoptions['view_sigs'], FALSE ) );
/* Profile */
$form->addHeader('member_preferences_profile');
$form->add( new \IPS\Helpers\Form\YesNo( 'pp_setting_count_visitors', $member->members_bitoptions['pp_setting_count_visitors'], FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'pp_setting_moderate_followers', !$member->members_bitoptions['pp_setting_moderate_followers'] ) );
}
/**
@@ -107,5 +112,7 @@ class _Preferences
/* Other */
$member->members_bitoptions['view_sigs'] = $values['view_sigs'];
$member->members_bitoptions['pp_setting_count_visitors'] = $values['pp_setting_count_visitors'];
$member->members_bitoptions['pp_setting_moderate_followers'] = $values['pp_setting_moderate_followers'] ? FALSE : TRUE;
}
}
@@ -8,146 +8,4 @@
* @since 08 Apr 2013
*/
namespace IPS\core\extensions\core\MemberForm;
/* 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;
}
/**
* Admin CP Member Form
*/
class _Profile
{
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
/* Profile Preferences */
$form->addHeader('member_preferences_profile');
$form->add( new \IPS\Helpers\Form\Upload( 'pp_cover_photo', $member->pp_cover_photo ? \IPS\File::get( 'core_Profile', $member->pp_cover_photo ) : NULL, FALSE, array( 'storageExtension' => 'core_Profile', 'image' => TRUE ) ) );
$form->add( new \IPS\Helpers\Form\Text( 'member_title', $member->member_title, FALSE, array( 'maxLength' => 64 ) ) );
if ( \IPS\Settings::i()->profile_birthday_type !== 'none' )
{
$form->add( new \IPS\Helpers\Form\Custom( 'bday', array( 'year' => $member->bday_year, 'month' => $member->bday_month, 'day' => $member->bday_day ), FALSE, array( 'getHtml' => function( $element )
{
return strtr( \IPS\Member::loggedIn()->language()->preferredDateFormat(), array(
'DD' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_day( $element->name, $element->value, $element->error ),
'MM' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_month( $element->name, $element->value, $element->error ),
'YY' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_year( $element->name, $element->value, $element->error ),
'YYYY' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_year( $element->name, $element->value, $element->error ),
) );
} ) ) );
}
if ( \IPS\Settings::i()->signatures_enabled )
{
$form->add( new \IPS\Helpers\Form\Editor( 'signature', $member->signature, FALSE, array( 'app' => 'core', 'key' => 'Signatures', 'autoSaveKey' => "sig-{$member->member_id}", 'attachIds' => array( $member->member_id ) ) ) );
}
$form->add( new \IPS\Helpers\Form\YesNo( 'pp_setting_count_visitors', $member->members_bitoptions['pp_setting_count_visitors'], FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'pp_setting_moderate_followers', !$member->members_bitoptions['pp_setting_moderate_followers'] ) );
/* Profile Fields */
try
{
$values = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $member->member_id ) )->first();
}
catch( \UnderflowException $e )
{
$values = array();
}
if( count( $values ) )
{
foreach ( \IPS\core\ProfileFields\Field::fields( $values, \IPS\core\ProfileFields\Field::STAFF ) as $group => $fields )
{
$form->addHeader( "core_pfieldgroups_{$group}" );
foreach ( $fields as $field )
{
$form->add( $field );
}
}
}
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
/* Profile Preferences */
$member->member_title = $values['member_title'];
if ( $values['bday'] )
{
$member->bday_day = $values['bday']['day'];
$member->bday_month = $values['bday']['month'];
$member->bday_year = $values['bday']['year'];
}
else
{
$member->bday_day = NULL;
$member->bday_month = NULL;
$member->bday_year = NULL;
}
if ( \IPS\Settings::i()->signatures_enabled )
{
$member->signature = $values['signature'];
}
$member->members_bitoptions['pp_setting_count_visitors'] = $values['pp_setting_count_visitors'];
$member->members_bitoptions['pp_setting_moderate_followers'] = $values['pp_setting_moderate_followers'] ? FALSE : TRUE;
/* Cover photo */
if ( $values['pp_cover_photo'] )
{
$member->pp_cover_photo = (string) $values['pp_cover_photo'];
}
else
{
$member->pp_cover_photo = '';
}
/* Profile Fields */
try
{
$profileFields = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $member->member_id ) )->first();
}
catch( \UnderflowException $e )
{
$profileFields = array();
}
/* If \IPS\Db::i()->select()->first() has only one column, then the contents of that column is returned. We do not want this here. */
if ( !is_array( $profileFields ) )
{
$profileFields = array();
}
$profileFields['member_id'] = $member->member_id;
foreach ( \IPS\core\ProfileFields\Field::fields( $profileFields, \IPS\core\ProfileFields\Field::STAFF ) as $group => $fields )
{
foreach ( $fields as $id => $field )
{
$profileFields[ "field_{$id}" ] = $field::stringValue( !empty( $values[ $field->name ] ) ? $values[ $field->name ] : NULL );
}
}
\IPS\Db::i()->replace( 'core_pfields_content', $profileFields );
$member->changedCustomFields = $profileFields;
}
}
// Removed
@@ -1,137 +1,3 @@
<?php
/**
* @brief Admin CP Member Form
* @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 15 Apr 2013
*/
namespace IPS\core\extensions\core\MemberForm;
/* 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;
}
/**
* Admin CP Member Form
*/
class _Restrictions
{
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
/* Moderation */
$form->addHeader( 'member_moderation' );
if ( \IPS\Settings::i()->warn_on )
{
$form->add( new \IPS\Helpers\Form\Number( 'member_warnings', $member->warn_level, FALSE ) );
}
$form->add( new \IPS\Helpers\Form\Date( 'restrict_post', $member->restrict_post, FALSE, array( 'time' => TRUE, 'unlimited' => -1, 'unlimitedLang' => 'indefinitely' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('until') ) );
$form->add( new \IPS\Helpers\Form\Date( 'mod_posts', $member->mod_posts, FALSE, array( 'time' => TRUE, 'unlimited' => -1, 'unlimitedLang' => 'indefinitely' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('until') ) );
/* Restrictions */
$form->addHeader( 'member_restrictions' );
if ( \IPS\Settings::i()->tags_enabled )
{
if ( !$member->group['gbw_disable_tagging'] )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_disable_tagging', !$member->members_bitoptions['bw_disable_tagging'] ) );
}
if ( !$member->group['gbw_disable_prefixes'] )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_disable_prefixes', !$member->members_bitoptions['bw_disable_prefixes'] ) );
}
}
if ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
{
/* There are two columns for "can post status updates". The first is a user-level option (the user can turn status updates on and
off in their account), and the second is an admin-level option (block user from posting status updates). Having two options is
confusing so we merge both options into one select list which matches the PM dropdown below */
$value = ( !$member->members_bitoptions['bw_no_status_update'] AND $member->pp_setting_count_comments ) ? 0 :
( !$member->members_bitoptions['bw_no_status_update'] ? 1 : 2 );
$form->add( new \IPS\Helpers\Form\Select( 'bw_no_status_update', $value, FALSE, array( 'options' => array(
0 => 'members_disable_pm_on',
1 => 'members_disable_pm_member_disable',
2 => 'members_disable_pm_admin_disable',
) ) ) );
}
if ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'messaging', 'front' ) ) )
{
$form->add( new \IPS\Helpers\Form\Select( 'members_disable_pm', $member->members_disable_pm, FALSE, array( 'options' => array(
0 => 'members_disable_pm_on',
1 => 'members_disable_pm_member_disable',
2 => 'members_disable_pm_admin_disable',
) ) ) );
}
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
if ( \IPS\Settings::i()->warn_on )
{
$member->warn_level = $values['member_warnings'];
}
$member->mod_posts = is_object( $values['mod_posts'] ) ? $values['mod_posts']->getTimestamp() : ( $values['mod_posts'] ?: 0 );
$member->restrict_post = is_object( $values['restrict_post'] ) ? $values['restrict_post']->getTimestamp() : ( $values['restrict_post'] ?: 0 );
if ( \IPS\Settings::i()->tags_enabled )
{
if ( !$member->group['gbw_disable_tagging'] AND array_key_exists( 'bw_disable_tagging', $values ) )
{
$member->members_bitoptions['bw_disable_tagging'] = !$values['bw_disable_tagging'];
}
if ( !$member->group['gbw_disable_prefixes'] AND array_key_exists( 'bw_disable_prefixes', $values ) )
{
$member->members_bitoptions['bw_disable_prefixes'] = !$values['bw_disable_prefixes'];
}
}
if ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
{
/* We may be adding a secondary group which DOES have access - we should just use the default in this instance */
if ( array_key_exists( 'bw_no_status_update', $values ) )
{
/* 0 means 'yes can post', 1 means 'no, but user can enable' and 2 means 'no and user cannot re-enable'. */
$member->members_bitoptions['bw_no_status_update'] = ( $values['bw_no_status_update'] == 2 ) ? 1 : 0;
$member->pp_setting_count_comments = ( $values['bw_no_status_update'] == 0 ) ? 1 : 0;
}
else
{
$member->members_bitoptions['bw_no_status_update'] = FALSE;
}
}
if ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'messaging', 'front' ) ) )
{
/* We may be adding a secondary group which DOES have access - we should just use the default in this instance */
if ( array_key_exists( 'members_disable_pm', $values ) )
{
$member->members_disable_pm = $values['members_disable_pm'];
}
else
{
$member->members_disable_pm = 0;
}
}
}
}
// Removed
@@ -8,128 +8,4 @@
* @since 28 Sep 2016
*/
namespace IPS\core\extensions\core\MemberForm;
/* 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;
}
/**
* Admin CP Member Form: MFA
*/
class _SecurityAnswers // It's called this for backwards compatibility from when we just had security answers
{
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
/* ACP restriction check */
if ( !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_mfa' ) )
{
return;
}
/* Get all the fields we'll need */
$fields = array();
$optOutToggles = array();
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $member ) )
{
foreach ( $handler->acpConfiguration( $member ) as $id => $field )
{
$fields[] = $field;
}
$optOutToggles[] = "mfa_{$key}_title";
}
}
/* If we don't have have any, we need don't to display this tab */
if ( !count( $fields ) )
{
return;
}
/* We need the opt-out field if the user is in a group which can opt out */
if ( \IPS\Settings::i()->mfa_required_groups != '*' and !$member->inGroup( explode( ',', \IPS\Settings::i()->mfa_required_groups ) ) )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'mfa_opt_out_admin', $member->members_bitoptions['security_questions_opt_out'], FALSE, array( 'togglesOff' => $optOutToggles ) ) );
}
/* Now add all the other fields */
foreach ( $fields as $id => $field )
{
if ( $field instanceof \IPS\Helpers\Form\Matrix )
{
$form->addMatrix( $field->id, $field );
}
else
{
$form->add( $field );
}
}
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
/* ACP restriction check */
if ( !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_mfa' ) )
{
return;
}
/* Reset the failure count and unlock if necessary */
$member->failed_mfa_attempts = 0;
$mfaDetails = $member->mfa_details;
if ( isset( $mfaDetails['_lockouttime'] ) )
{
unset( $mfaDetails['_lockouttime'] );
$member->mfa_details = $mfaDetails;
}
/* Did we opt out? */
if ( isset( $values['mfa_opt_out_admin'] ) )
{
/* Opt-Out: Disable all handlers */
if ( $values['mfa_opt_out_admin'] )
{
$member->members_bitoptions['security_questions_opt_out'] = TRUE;
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
$handler->disableHandlerForMember( $member );
}
$member->save();
}
/* Opt-In */
else
{
$member->members_bitoptions['security_questions_opt_out'] = FALSE;
}
}
/* Save each of the handlers */
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $member ) )
{
$handler->acpConfigurationSave( $member, $values );
}
}
}
}
// Removed
@@ -30,18 +30,18 @@ class _Core
public function getTypes()
{
return array(
'photo',
'coverphoto',
'groups',
'login',
'password_change',
'display_name',
'email_change',
'group_promotion',
'group_promotion_o',
'member_group_id',
'merged',
'mfa',
'mgroup_others',
'password_change',
'spammer',
'social_account',
'warning'
'account',
'warning',
'mfa',
'terms_acceptance'
);
}
@@ -53,149 +53,616 @@ class _Core
* @return string
*/
public function parseLogData( $value, $row )
{
{
$jsonValue = json_decode( $value, TRUE );
$byMember = '';
$byStaff = '';
if ( $row['log_by'] )
{
if ( $row['log_by'] === $row['log_member'] )
{
$byMember = \IPS\Member::loggedIn()->language()->addToStack('history_by_member');
}
$byStaff = \IPS\Member::loggedIn()->language()->addToStack('history_by_admin', FALSE, array( 'sprintf' => array( \IPS\Member::load( $row['log_by'] )->name ) ) );
}
switch( $row['log_type'] )
{
case 'display_name':
$oldDisplayName = $jsonValue['old'] ?: \IPS\Member::loggedIn()->language()->addToStack('history_unknown'); // Old display name records may be missing this
return \IPS\Member::loggedIn()->language()->addToStack('history_name_changed', FALSE, array( 'sprintf' => array( $oldDisplayName, $jsonValue['new'] ) ) );
break;
case 'email_change':
$newEmailAddress = $jsonValue['new'] ?: \IPS\Member::loggedIn()->language()->addToStack('history_unknown'); // Previous customer history didn't log what it was changed to
return \IPS\Member::loggedIn()->language()->addToStack('history_email_changed', FALSE, array( 'sprintf' => array( $jsonValue['old'], $newEmailAddress ) ) );
break;
case 'group_promotion':
case 'group_promotion_o':
if( $row['log_type'] == 'group_promotion' )
case 'photo':
if ( $jsonValue['action'] === 'new' )
{
try
if ( $jsonValue['type'] === 'gallery' )
{
$groupName = \IPS\Member\Group::load( $jsonValue['new'] )->name;
try
{
$image = \IPS\gallery\Image::load( $jsonValue['id'] );
$link = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $image->url(), TRUE, \IPS\Member::loggedIn()->language()->addToStack('history_new_photo_gallery_link'), FALSE );
}
catch ( \OutOfRangeException $e )
{
$link = \IPS\Member::loggedIn()->language()->addToStack('history_new_photo_gallery_link');
}
return \IPS\Member::loggedIn()->language()->addToStack( 'history_new_photo_gallery', FALSE, array( 'htmlsprintf' => array( $link, $byMember ?: $byStaff ) ) );
}
catch( \UnderflowException $e)
elseif ( $jsonValue['type'] === 'profilesync' )
{
$groupName = \IPS\Member::loggedIn()->language()->addToStack('history_deleted_group');
try
{
$method = \IPS\Login\Handler::load( $jsonValue['id'] )->_title;
}
catch ( \OutOfRangeException $e )
{
$method = \IPS\Member::loggedIn()->language()->addToStack( $jsonValue['service'] );
}
return \IPS\Member::loggedIn()->language()->addToStack( 'history_new_photo_profilesync', FALSE, array( 'sprintf' => array( $method ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_new_photo_' . $jsonValue['type'], FALSE, array( 'sprintf' => array( $byMember ?: $byStaff ) ) );
}
}
else
{
$mgroups = array();
foreach( explode( ',', $jsonValue['new'] ) as $groupId )
return \IPS\Member::loggedIn()->language()->addToStack( 'history_photo_' . $jsonValue['action'], FALSE, array( 'sprintf' => array( $byMember ?: $byStaff ) ) );
}
break;
case 'coverphoto':
if ( isset( $jsonValue['type'] ) and $jsonValue['type'] == 'profilesync' )
{
try
{
$method = \IPS\Login\Handler::load( $jsonValue['id'] )->_title;
}
catch ( \OutOfRangeException $e )
{
$method = \IPS\Member::loggedIn()->language()->addToStack( $jsonValue['service'] );
}
return \IPS\Member::loggedIn()->language()->addToStack( 'history_cover_photo_profilesync', FALSE, array( 'sprintf' => array( $method ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_cover_photo_' . $jsonValue['action'], FALSE, array( 'sprintf' => array( $byMember ?: $byStaff ) ) );
}
break;
case 'group':
if ( $jsonValue['by'] == 'legacy' )
{
$jsonValue['new'] = explode( ',', $jsonValue['data']['new'] );
$jsonValue['old'] = array();
}
foreach ( array( 'old', 'new' ) as $k )
{
$$k = array();
foreach ( is_array( $jsonValue[ $k ] ) ? $jsonValue[ $k ] : array( $jsonValue[ $k ] ) as $id )
{
try
{
$mgroups[] = \IPS\Member\Group::load( trim( $groupId ) )->name;
${$k}[] = \IPS\Theme::i()->getTemplate( 'members', 'core' )->groupLink( \IPS\Member\Group::load( $id ) );
}
catch( \UnderflowException $e)
catch ( \OutOfRangeException $e )
{
$mgroups[] = \IPS\Member::loggedIn()->language()->addToStack('history_deleted_group');
${$k}[] = \IPS\Member::loggedIn()->language()->addToStack( 'history_deleted_group_id', FALSE, array( 'sprintf' => array( $id ) ) );
}
}
$groupName = \IPS\Member::loggedIn()->language()->formatList( $mgroups );
if ( $$k )
{
$$k = \IPS\Member::loggedIn()->language()->formatList( $$k );
}
else
{
$$k = \IPS\Member::loggedIn()->language()->addToStack('history_no_groups');
}
}
try
switch ( $jsonValue['by'] )
{
$reason = \IPS\Member\GroupPromotion::load( $jsonValue['reason'] )->_title;
}
catch( \OutOfRangeException $e )
{
$reason = \IPS\Member::loggedIn()->language()->addToStack('history_deleted_grouppromotion');
}
case 'subscription':
try
{
$subs = \IPS\nexus\Subscription\Package::load( $jsonValue['id'] )->_title;
}
catch ( \OutOfRangeException $e )
{
$subs = '';
}
switch ( $jsonValue['action'] )
{
case 'add':
case 'remove':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_change_subscription_' . $jsonValue['action'], FALSE, array( 'htmlsprintf' => array( $subs, $old, $new ) ) );
}
case 'purchase':
try
{
$purchase = \IPS\Theme::i()->getTemplate('purchases', 'nexus')->link( \IPS\nexus\Purchase::load( $jsonValue['id'] ) );
}
catch ( \OutOfRangeException $e )
{
$purchase = $val['name'];
}
$type = \IPS\Member::loggedIn()->language()->addToStack( 'history_group_change_purchase_' . $jsonValue['type'] );
switch ( $jsonValue['action'] )
{
case 'add':
case 'remove':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_change_purchase_' . $jsonValue['action'], FALSE, array( 'htmlsprintf' => array( $purchase, $type, $old, $new ) ) );
return \IPS\Member::loggedIn()->language()->addToStack('history_group_promotion', FALSE, array( 'sprintf' => array( $groupName, $reason ) ) );
case 'change':
try
{
$expiringPurchase = \IPS\Theme::i()->getTemplate('purchases', 'nexus')->link( \IPS\nexus\Purchase::load( $jsonValue['remove_id'] ), TRUE );
}
catch ( \OutOfRangeException $e )
{
$expiringPurchase = $val['remove_name'];
}
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_change_purchase_' . $jsonValue['action'], FALSE, array( 'htmlsprintf' => array( $expiringPurchase, $purchase, $type, $old, $new ) ) );
}
case 'manual':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_change_' . $jsonValue['type'], FALSE, array( 'htmlsprintf' => array( $old, $new ), 'sprintf' => array( $byStaff ) ) );
case 'mass':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_change_mass', FALSE, array( 'htmlsprintf' => array( $old, $new ), 'sprintf' => array( $byStaff ) ) );
case 'api':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_change_api_' . $jsonValue['type'], FALSE, array( 'htmlsprintf' => array( $old, $new ) ) );
case 'promotion':
try
{
$rule = \IPS\Member\GroupPromotion::load( $jsonValue['id'] )->_title;
}
catch ( \OutOfRangeException $e )
{
$rule = '#' . $jsonValue['id'];
}
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_change_promotion_' . $jsonValue['type'], FALSE, array( 'htmlsprintf' => array( $old, $new ), 'sprintf' => array( $rule ) ) );
case 'legacy':
if ( in_array( $jsonValue['type'], array( 'group_promotion', 'group_promotion_o' ) ) )
{
try
{
$rule = \IPS\Member\GroupPromotion::load( $jsonValue['data']['reason'] )->_title;
}
catch ( \OutOfRangeException $e )
{
$rule = '#' . $jsonValue['id'];
}
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_legacy_' . $jsonValue['type'], FALSE, array( 'htmlsprintf' => array( $new ), 'sprintf' => array( $rule ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_group_legacy_' . $jsonValue['type'], FALSE, array( 'htmlsprintf' => array( $new ) ) );
}
}
break;
case 'member_group_id':
try
case 'login':
switch ( $jsonValue['type'] )
{
$groupName = \IPS\Member\Group::load( $jsonValue['new'] )->name;
case 'new_device':
case 'logout':
try
{
$device = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( \IPS\Http\Url::internal("app=core&module=members&controller=devices&do=device&key={$jsonValue['device']}&member={$row['log_member']}"), FALSE, (string) \IPS\Member\Device::load( $jsonValue['device'] )->userAgent(), FALSE );
}
catch ( \Exception $e )
{
$device = $jsonValue['device'];
}
if ( $jsonValue['type'] == 'new_device' )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_new_device', FALSE, array( 'htmlsprintf' => array( $device ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_device_logout', FALSE, array( 'htmlsprintf' => array( $device, $byMember ?: $byStaff ) ) );
}
case 'lock':
if ( isset( $jsonValue['unlockTime'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_locked_time', FALSE, array( 'htmlsprintf' => array( \IPS\DateTime::ts( $jsonValue['unlockTime'] ), $jsonValue['count'] ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_locked', FALSE, array( 'htmlsprintf' => array( $jsonValue['count'] ) ) );
}
case 'mfalock':
if ( isset( $jsonValue['unlockTime'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_mfa_locked_time', FALSE, array( 'htmlsprintf' => array( \IPS\DateTime::ts( $jsonValue['unlockTime'] ), $jsonValue['count'] ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_mfa_locked', FALSE, array( 'htmlsprintf' => array( $jsonValue['count'] ) ) );
}
case 'unlock':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_unlocked', FALSE, array( 'htmlsprintf' => array( $byStaff ) ) );
}
catch( \UnderflowException $e)
break;
case 'password_change':
switch ( $jsonValue )
{
$groupName = \IPS\Member::loggedIn()->language()->addToStack('history_deleted_group');
case 'lost':
return \IPS\Member::loggedIn()->language()->addToStack('history_password_changed_lost');
case 'api':
return \IPS\Member::loggedIn()->language()->addToStack('history_password_changed_api');
default:
return \IPS\Member::loggedIn()->language()->addToStack( 'history_password_changed', FALSE, array( 'sprintf' => array( $byMember ?: $byStaff ) ) );
}
break;
case 'display_name':
$oldDisplayName = $jsonValue['old'] ?: \IPS\Member::loggedIn()->language()->addToStack('history_unknown'); // Old display name records may be missing this
switch ( isset( $jsonValue['by'] ) ? $jsonValue['by'] : NULL )
{
case 'manual':
return \IPS\Member::loggedIn()->language()->addToStack('history_name_changed_manual', FALSE, array( 'sprintf' => array( $jsonValue['new'], $oldDisplayName, $byMember ?: $byStaff ) ) );
case 'api':
return \IPS\Member::loggedIn()->language()->addToStack('history_name_changed_api', FALSE, array( 'sprintf' => array( $jsonValue['new'], $oldDisplayName ) ) );
case 'profilesync':
try
{
$method = \IPS\Login\Handler::load( $jsonValue['id'] )->_title;
}
catch ( \OutOfRangeException $e )
{
$method = \IPS\Member::loggedIn()->language()->addToStack( $jsonValue['service'] );
}
return \IPS\Member::loggedIn()->language()->addToStack('history_name_changed_profilesync', FALSE, array( 'sprintf' => array( $jsonValue['new'], $oldDisplayName, $method ) ) );
default:
return \IPS\Member::loggedIn()->language()->addToStack('history_name_changed', FALSE, array( 'sprintf' => array( $jsonValue['new'], $oldDisplayName ) ) );
}
break;
case 'email_change':
$newEmailAddress = $jsonValue['new'] ?: \IPS\Member::loggedIn()->language()->addToStack('history_unknown'); // Previous customer history didn't log what it was changed to
switch ( isset( $jsonValue['by'] ) ? $jsonValue['by'] : NULL )
{
case 'manual':
return \IPS\Member::loggedIn()->language()->addToStack('history_email_changed_manual', FALSE, array( 'sprintf' => array( $newEmailAddress, $jsonValue['old'], $byMember ?: $byStaff ) ) );
case 'api':
return \IPS\Member::loggedIn()->language()->addToStack('history_email_changed_api', FALSE, array( 'sprintf' => array( $newEmailAddress, $jsonValue['old'] ) ) );
case 'profilesync':
try
{
$method = \IPS\Login\Handler::load( $jsonValue['id'] )->_title;
}
catch ( \OutOfRangeException $e )
{
$method = \IPS\Member::loggedIn()->language()->addToStack( $jsonValue['service'] );
}
return \IPS\Member::loggedIn()->language()->addToStack('history_email_changed_profilesync', FALSE, array( 'sprintf' => array( $newEmailAddress, $jsonValue['old'], $method ) ) );
default:
return \IPS\Member::loggedIn()->language()->addToStack('history_email_changed', FALSE, array( 'sprintf' => array( $newEmailAddress, $jsonValue['old'] ) ) );
}
break;
case 'social_account':
$handler = NULL;
if ( isset( $jsonValue['handler'] ) )
{
try
{
$handler = \IPS\Login\Handler::load( $jsonValue['handler'] );
$handlerName = $handler->_title;
}
catch ( \OutOfRangeException $e )
{
$handlerName = \IPS\Member::loggedIn()->language()->addToStack( $jsonValue['service'] );
}
}
else
{
$handlerName = \IPS\Member::loggedIn()->language()->addToStack( 'login_handler_' . mb_ucfirst( $jsonValue['service'] ) );
}
if ( isset( $jsonValue['changed'] ) )
{
$changes = array();
foreach ( $jsonValue['changed'] as $k => $v )
{
$changes[] = \IPS\Member::loggedIn()->language()->addToStack( 'history_social_sync_changed_' . $k, FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $v ? 'history_social_sync_enabled' : 'history_social_sync_disabled' ) ) ) );
}
return \IPS\Member::loggedIn()->language()->addToStack('history_social_sync_changes', FALSE, array( 'sprintf' => array( $handlerName, $byMember ?: $byStaff, implode( '; ', $changes ) ) ) );
}
$account = NULL;
$link = NULL;
if ( $handler and isset( $jsonValue['account_id'] ) or isset( $jsonValue['account_name'] ) )
{
$account = $jsonValue['account_name'];
if ( $link = $handler->userLink( $jsonValue['account_id'], $jsonValue['account_name'] ) )
{
$link = (string) $link->makeSafeForAcp();
}
}
if( isset( $jsonValue['registered'] ) or $jsonValue['linked'] === TRUE )
{
$type = 'connected';
}
else
{
$type = 'disconnected';
}
if ( $account )
{
if ( $link )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_'.$type.'_social_with_link', FALSE, array( 'sprintf' => array( $handlerName, $link, $account, $byMember ?: $byStaff ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack('history_'.$type.'_social_with_account', FALSE, array( 'sprintf' => array( $handlerName, $account, $byMember ?: $byStaff ) ) );
}
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack('history_'.$type.'_social', FALSE, array( 'sprintf' => array( $handlerName, $byMember ?: $byStaff ) ) );
}
break;
case 'account':
$spamDefenseScore = NULL;
if ( isset( $jsonValue['spamCode'] ) )
{
$spamDefenseScore = \IPS\Member::loggedIn()->language()->addToStack( 'history_spam_defense_' . $jsonValue['spamAction'], FALSE, array( 'sprintf' => array( $jsonValue['spamCode'] ) ) );
}
switch ( $jsonValue['type'] )
{
case 'register':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_register', FALSE, array( 'sprintf' => array( $spamDefenseScore ) ) );
case 'register_checkout':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_register_checkout', FALSE, array( 'sprintf' => array( $spamDefenseScore ) ) );
case 'register_admin':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_register_admin', FALSE, array( 'sprintf' => array( $byStaff ) ) );
case 'register_handler':
try
{
$method = \IPS\Login\Handler::load( $jsonValue['handler'] )->_title;
}
catch ( \OutOfRangeException $e )
{
$method = \IPS\Member::loggedIn()->language()->addToStack( $jsonValue['service'] );
}
return \IPS\Member::loggedIn()->language()->addToStack( $jsonValue['complete'] ? 'history_account_created_handler' : 'history_account_created_handler_incomplete', FALSE, array( 'sprintf' => array( $method, $spamDefenseScore ) ) );
case 'complete':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_completed', FALSE, array( 'sprintf' => array( $byMember ?: $byStaff, $spamDefenseScore ) ) );
case 'email_validated':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_email_validated' );
case 'admin_validated':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_admin_validated', FALSE, array( 'sprintf' => array( $byStaff ) ) );
case 'merge':
if ( isset( $jsonValue['id'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_merged', FALSE, array( 'sprintf' => array( $jsonValue['id'], $jsonValue['name'], $jsonValue['email'], $byStaff ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_account_merged_legacy', FALSE, array( 'sprintf' => array( $jsonValue['legacy']['old'], $byStaff ) ) );
}
case 'spammer':
$set = isset( $jsonValue['set'] ) ? $jsonValue['set'] : $jsonValue['legacy']['set'];
if ( $set and isset( $jsonValue['actions'] ) )
{
$flagActions = array();
if ( in_array( 'delete', $jsonValue['actions'] ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('history_flagged_spammer_action_delete');
}
elseif ( in_array( 'unapprove', $jsonValue['actions'] ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('history_flagged_spammer_action_unapprove');
}
if ( in_array( 'ban', $jsonValue['actions'] ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('history_flagged_spammer_action_ban');
}
elseif ( in_array( 'disable', $jsonValue['actions'] ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('history_flagged_spammer_action_disable');
}
if ( count( $flagActions ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_flagged_spammer_with_actions', FALSE, array( 'sprintf' => array( $byStaff, \IPS\Member::loggedIn()->language()->formatList( $flagActions ) ) ) );
}
}
return \IPS\Member::loggedIn()->language()->addToStack( $set ? 'history_flagged_spammer' : 'history_unflagged_spammer', FALSE, array( 'sprintf' => array( $byStaff ) ) );
}
break;
case 'warning':
if ( isset( $jsonValue['restrictions'] ) ) // Manual restrictions change
{
$changes = array();
if ( isset( $jsonValue['restrictions']['member_warnings'] ) )
{
$changes[] = \IPS\Member::loggedIn()->language()->addToStack( 'history_restrictions_warning_level', FALSE, array( 'sprintf' => array( $jsonValue['restrictions']['member_warnings']['new'], $jsonValue['restrictions']['member_warnings']['old'] ) ) );
}
if ( isset( $jsonValue['restrictions']['ban'] ) )
{
if ( $jsonValue['restrictions']['ban']['new'] )
{
$c = \IPS\Member::loggedIn()->language()->addToStack( 'moderation_banned' );
if ( $jsonValue['restrictions']['ban']['new'] != -1 )
{
$diff = \IPS\DateTime::ts( $row['log_date'] )->diff( \IPS\DateTime::ts( $jsonValue['restrictions']['ban']['new'] ) );
$c = \IPS\Member::loggedIn()->language()->addToStack( 'history_received_warning_penalty_time', FALSE, array( 'sprintf' => array( $c, \IPS\DateTime::formatInterval( $diff, 2 ) ) ) );
}
}
else
{
$c = \IPS\Member::loggedIn()->language()->addToStack( 'history_warning_revoke_temp_ban' );
}
$changes[] = $c;
}
if ( isset( $jsonValue['restrictions']['members_disable_pm'] ) )
{
$changes[] = \IPS\Member::loggedIn()->language()->addToStack( 'history_restrictions_messenger_' . intval( $jsonValue['restrictions']['members_disable_pm']['new'] ) );
}
return \IPS\Member::loggedIn()->language()->addToStack('history_member_group', FALSE, array( 'sprintf' => array( $groupName ) ) );
break;
case 'merged':
return \IPS\Member::loggedIn()->language()->addToStack('history_member_merged', FALSE, array( 'sprintf' => array( $jsonValue['old'] ) ) );
foreach ( \IPS\Application::allExtensions( 'core', 'MemberRestrictions', TRUE, 'core', 'Content', FALSE ) as $class )
{
foreach ( $class::changesForHistory( $jsonValue['restrictions'] ) as $v )
{
$changes[] = $v;
}
}
return \IPS\Member::loggedIn()->language()->addToStack( 'history_restrictions_change', FALSE, array( 'sprintf' => array( $byStaff, \IPS\Member::loggedIn()->language()->formatList( $changes ) ) ) );
}
elseif ( isset( $jsonValue['type'] ) and $jsonValue['type'] == 'revoke' ) // Warning revoked
{
$consequences = array();
if ( isset( $jsonValue['consequences'] ) )
{
foreach ( array( 'modq' => 'mod_posts', 'nopost' => 'restrict_post', 'banned' => 'temp_ban' ) as $k => $v )
{
if ( isset( $jsonValue['consequences'][ $v ] ) )
{
$consequences[] = \IPS\Member::loggedIn()->language()->addToStack( 'history_warning_revoke_' . $v );
}
}
}
$consequences = count( $consequences ) ? \IPS\Member::loggedIn()->language()->formatList( $consequences ) : \IPS\Member::loggedIn()->language()->addToStack('history_received_warning_no_changes');
return \IPS\Member::loggedIn()->language()->addToStack( 'history_revoke_warning', FALSE, array( 'sprintf' => array( $byStaff, $consequences ) ) );
}
else // Warning given
{
$consequences = array();
if ( isset( $jsonValue['consequences'] ) )
{
foreach ( array( 'modq' => 'mod_posts', 'nopost' => 'restrict_post', 'banned' => 'temp_ban' ) as $k => $v )
{
if ( isset( $jsonValue['consequences'][ $v ] ) )
{
$c = \IPS\Member::loggedIn()->language()->addToStack( 'moderation_' . $k );
if ( $jsonValue['consequences'][ $v ] != -1 )
{
$c = \IPS\Member::loggedIn()->language()->addToStack( 'history_received_warning_penalty_time', FALSE, array( 'sprintf' => array( $c, \IPS\DateTime::formatInterval( new \DateInterval( $jsonValue['consequences'][ $v ] ), 2 ) ) ) );
}
$consequences[] = $c;
}
}
}
$consequences = count( $consequences ) ? \IPS\Member::loggedIn()->language()->formatList( $consequences ) : \IPS\Member::loggedIn()->language()->addToStack('history_received_warning_no_penalties');
try
{
$warning = \IPS\core\Warnings\Warning::load( $jsonValue['wid'] );
if ( $warning->canViewDetails() )
{
if ( isset( $jsonValue['points'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_received_warning_link', FALSE, array( 'sprintf' => array( \IPS\Http\Url::internal("app=core&module=members&controllers=members&do=viewWarning&id={$warning->id}"), $jsonValue['points'], \IPS\Member::loggedIn()->language()->addToStack( 'core_warn_reason_' . $jsonValue['reason'] ), $byStaff, $consequences ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack('history_received_warning_legacy_link', FALSE, array( 'sprintf' => array( \IPS\Http\Url::internal("app=core&module=members&controllers=members&do=viewWarning&id={$warning->id}") ) ) );
}
}
}
catch ( \Exception $e ) { }
if ( isset( $jsonValue['points'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_received_warning_details', FALSE, array( 'sprintf' => array( $jsonValue['points'], \IPS\Member::loggedIn()->language()->addToStack( 'core_warn_reason_' . $jsonValue['reason'] ), $byStaff, $consequences ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_received_warning', FALSE, array( 'sprintf' => array( $byStaff ) ) );
}
}
break;
case 'mfa':
$handlerName = \IPS\Member::loggedIn()->language()->addToStack('mfa_' . $jsonValue['handler'] . '_title');
if( $jsonValue['enable'] === TRUE )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_mfa_enabled', FALSE, array( 'sprintf' => array( $handlerName ) ) );
if ( isset( $jsonValue['reconfigure'] ) and $jsonValue['reconfigure'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_mfa_reconfigured', FALSE, array( 'sprintf' => array( $handlerName, $byMember ?: $byStaff ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_mfa_enabled', FALSE, array( 'sprintf' => array( $handlerName, $byMember ?: $byStaff ) ) );
}
}
if( isset( $jsonValue['optout'] ) AND $jsonValue['optout'] === TRUE )
if( isset( $jsonValue['optout'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_mfa_optout');
return \IPS\Member::loggedIn()->language()->addToStack( $jsonValue['optout'] ? 'history_mfa_optout' : 'history_mfa_optin', FALSE, array( 'sprintf' => array( $byMember ?: $byStaff ) ) );
}
return \IPS\Member::loggedIn()->language()->addToStack('history_mfa_disabled', FALSE, array( 'sprintf' => array( $handlerName ) ) );
return \IPS\Member::loggedIn()->language()->addToStack( 'history_mfa_disabled', FALSE, array( 'sprintf' => array( $handlerName, $byMember ?: $byStaff ) ) );
break;
case 'mgroup_others':
$groups = explode( ',', $jsonValue['new'] );
if( count( $groups ) )
{
array_walk( $groups, function( &$id )
{
try
{
$id = \IPS\Member\Group::load( $id )->name;
}
catch( \OutOfRangeException $e)
{
$id = \IPS\Member::loggedIn()->language()->addToStack('history_deleted_group');
}
});
}
else
{
$groups = array( \IPS\Member::loggedIn()->language()->addToStack('history_no_group') );
}
return \IPS\Member::loggedIn()->language()->addToStack('history_member_secondary_groups', FALSE, array( 'sprintf' => array( implode( ', ', $groups ) ) ) );
break;
case 'password_change':
return \IPS\Member::loggedIn()->language()->addToStack('history_password_changed');
break;
case 'spammer':
if( $jsonValue['set'] === TRUE )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_flagged_spammer');
}
return \IPS\Member::loggedIn()->language()->addToStack('history_unflagged_spammer');
break;
case 'social_account':
$handlerName = \IPS\Member::loggedIn()->language()->addToStack('login_handler_' . ucfirst( $jsonValue['service'] ) );
if( $jsonValue['linked'] === TRUE )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_connected_social', FALSE, array( 'sprintf' => array( $handlerName ) ) );
}
elseif( isset( $jsonValue['registered'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_registered_social', FALSE, array( 'sprintf' => array( $handlerName ) ) );
}
return \IPS\Member::loggedIn()->language()->addToStack('history_disconnected_social', FALSE, array( 'sprintf' => array( $handlerName ) ) );
break;
case 'warning':
case 'oauth':
try
{
$warning = \IPS\core\Warnings\Warning::load( $jsonValue['wid'] );
return \IPS\Member::loggedIn()->language()->addToStack('history_received_warning_link', FALSE, array( 'sprintf' => array( $warning->url() ) ) );
$client = \IPS\Api\OAuthClient::load( $jsonValue['client'] );
$client = \IPS\Theme::i()->getTemplate( 'api', 'core' )->oauthClientLink( $client );
}
catch( \UnderflowExpection $e )
catch ( \OutOfRangeException $e )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_received_warning');
$client = \IPS\Theme::i()->getTemplate( 'api', 'core' )->apiKey( $jsonValue['client'] );
}
switch ( $jsonValue['type'] )
{
case 'issued_access_token':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_oauth_token_issued', FALSE, array( 'htmlsprintf' => array( $client, \IPS\Member::loggedIn()->language()->addToStack( 'history_oauth_token_issued_' . $jsonValue['grant'] ) ) ) );
case 'revoked_access_token':
return \IPS\Member::loggedIn()->language()->addToStack( 'history_oauth_token_revoked', FALSE, array( 'htmlsprintf' => array( $client, $byMember ?: $byStaff ) ) );
}
break;
case 'admin_mails':
if( $jsonValue['enabled'] === TRUE )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'history_enabled_admin_mails', FALSE, array( 'htmlsprintf' => array( $byMember ?: $byStaff ) ) );
}
return \IPS\Member::loggedIn()->language()->addToStack( 'history_disabled_admin_mails', FALSE, array( 'htmlsprintf' => array( $byMember ?: $byStaff ) ) );
break;
case 'terms_acceptance':
if( $jsonValue['type'] == 'privacy' )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_terms_accepted_privacy');
}
if( $jsonValue['type'] == 'terms' )
{
return \IPS\Member::loggedIn()->language()->addToStack('history_terms_accepted_terms');
}
break;
}
@@ -0,0 +1,117 @@
<?php
/**
* @brief Member Restrictions: 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 30 Nov 2017
*/
namespace IPS\core\extensions\core\MemberRestrictions;
/* 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;
}
/**
* @brief Member Restrictions: Content
*/
class _Content extends \IPS\core\MemberACPProfile\Restriction
{
/**
* Modify Edit Restrictions form
*
* @param \IPS\Helpers\Form $form The form
* @return void
*/
public function form( \IPS\Helpers\Form $form )
{
$form->add( new \IPS\Helpers\Form\Date( 'restrict_post', $this->member->restrict_post, FALSE, array( 'time' => TRUE, 'unlimited' => -1, 'unlimitedLang' => 'indefinitely' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('until') ) );
$form->add( new \IPS\Helpers\Form\Date( 'mod_posts', $this->member->mod_posts, FALSE, array( 'time' => TRUE, 'unlimited' => -1, 'unlimitedLang' => 'indefinitely' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('until') ) );
}
/**
* Save Form
*
* @param array $values Values from form
* @return array
*/
public function save( $values )
{
$return = array();
$modPosts = is_object( $values['mod_posts'] ) ? $values['mod_posts']->getTimestamp() : ( $values['mod_posts'] ?: 0 );
if ( $modPosts != $this->member->mod_posts )
{
$return['mod_posts'] = array( 'old' => $this->member->mod_posts, 'new' => $modPosts );
$this->member->mod_posts = $modPosts;
}
$restrictPost = is_object( $values['restrict_post'] ) ? $values['restrict_post']->getTimestamp() : ( $values['restrict_post'] ?: 0 );
if ( $restrictPost != $this->member->restrict_post )
{
$return['restrict_post'] = array( 'old' => $this->member->restrict_post, 'new' => $restrictPost );
$this->member->restrict_post = $restrictPost;
}
return $return;
}
/**
* What restrictions are active on the account?
*
* @return array
*/
public function activeRestrictions()
{
$return = array();
if ( $this->member->restrict_post )
{
$return[] = 'moderation_nopost';
}
elseif ( $this->member->mod_posts )
{
$return[] = 'moderation_modq';
}
return $return;
}
/**
* Get details of a change to show on history
*
* @param array $changes Changes as set in save()
* @return array
*/
public static function changesForHistory( $changes )
{
$return = array();
foreach ( array( 'modq' => 'mod_posts', 'nopost' => 'restrict_post', 'banned' => 'temp_ban' ) as $k => $v )
{
if ( isset( $changes[ $v ] ) )
{
if ( $changes[ $v ]['new'] )
{
$c = \IPS\Member::loggedIn()->language()->addToStack( 'moderation_' . $k );
if ( $changes[ $v ]['new'] != -1 )
{
$c = \IPS\Member::loggedIn()->language()->addToStack( 'history_received_warning_penalty_time', FALSE, array( 'sprintf' => array( $c, \IPS\DateTime::formatInterval( new \DateInterval( $changes[ $v ]['new'] ), 2 ) ) ) );
}
}
else
{
$c = \IPS\Member::loggedIn()->language()->addToStack( 'history_warning_revoke_' . $v );
}
$return[] = $c;
}
}
return $return;
}
}
@@ -0,0 +1,109 @@
<?php
/**
* @brief Member Restrictions: Statuses
* @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 30 Nov 2017
*/
namespace IPS\core\extensions\core\MemberRestrictions;
/* 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;
}
/**
* @brief Member Restrictions: Statuses
*/
class _Statuses extends \IPS\core\MemberACPProfile\Restriction
{
/**
* Is this extension available?
*
* @return bool
*/
public function enabled()
{
return $this->member->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) );
}
/**
* Modify Edit Restrictions form
*
* @param \IPS\Helpers\Form $form The form
* @return void
*/
public function form( \IPS\Helpers\Form $form )
{
/* There are two columns for "can post status updates". The first is a user-level option (the user can turn status updates on and
off in their account), and the second is an admin-level option (block user from posting status updates). Having two options is
confusing so we merge both options into one select list */
$value = ( !$this->member->members_bitoptions['bw_no_status_update'] AND $this->member->pp_setting_count_comments ) ? 0 :
( !$this->member->members_bitoptions['bw_no_status_update'] ? 1 : 2 );
$form->add( new \IPS\Helpers\Form\Radio( 'bw_no_status_update', $value, FALSE, array( 'options' => array(
0 => 'members_disable_pm_on',
1 => 'members_disable_pm_member_disable',
2 => 'members_disable_pm_admin_disable',
) ) ) );
}
/**
* Save Form
*
* @param array $values Values from form
* @return array
*/
public function save( $values )
{
$return = array();
$old = ( !$this->member->members_bitoptions['bw_no_status_update'] AND $this->member->pp_setting_count_comments ) ? 0 :
( !$this->member->members_bitoptions['bw_no_status_update'] ? 1 : 2 );
if ( $old != $values['bw_no_status_update'] )
{
$return['bw_no_status_update'] = array( 'old' => $old, 'new' => $values['bw_no_status_update'] );
$this->member->members_bitoptions['bw_no_status_update'] = ( $values['bw_no_status_update'] == 2 ) ? 1 : 0;
$this->member->pp_setting_count_comments = ( $values['bw_no_status_update'] == 0 ) ? 1 : 0;
}
return $return;
}
/**
* What restrictions are active on the account?
*
* @return array
*/
public function activeRestrictions()
{
$return = array();
if ( $this->member->members_bitoptions['bw_no_status_update'] )
{
$return[] = 'restriction_no_statuses';
}
return $return;
}
/**
* Get details of a change to show on history
*
* @param array $changes Changes as set in save()
* @return array
*/
public static function changesForHistory( $changes )
{
if ( isset( $changes['bw_no_status_update'] ) )
{
return array( \IPS\Member::loggedIn()->language()->addToStack( 'history_restrictions_status_updates_' . $changes['bw_no_status_update']['new'] ) );
}
return array();
}
}
@@ -0,0 +1,124 @@
<?php
/**
* @brief Member Restrictions: Tags
* @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 30 Nov 2017
*/
namespace IPS\core\extensions\core\MemberRestrictions;
/* 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;
}
/**
* @brief Member Restrictions: Tags
*/
class _Tags extends \IPS\core\MemberACPProfile\Restriction
{
/**
* Is this extension available?
*
* @return bool
*/
public function enabled()
{
return \IPS\Settings::i()->tags_enabled and ( !$this->member->group['gbw_disable_tagging'] or !$this->member->group['gbw_disable_prefixes'] );
}
/**
* Modify Edit Restrictions form
*
* @param \IPS\Helpers\Form $form The form
* @return void
*/
public function form( \IPS\Helpers\Form $form )
{
if ( !$this->member->group['gbw_disable_tagging'] )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_disable_tagging', !$this->member->members_bitoptions['bw_disable_tagging'] ) );
}
if ( !$this->member->group['gbw_disable_prefixes'] )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_disable_prefixes', !$this->member->members_bitoptions['bw_disable_prefixes'] ) );
}
}
/**
* Save Form
*
* @param array $values Values from form
* @return array
*/
public function save( $values )
{
$return = array();
if ( !$this->member->group['gbw_disable_tagging'] AND array_key_exists( 'bw_disable_tagging', $values ) )
{
if ( $this->member->members_bitoptions['bw_disable_tagging'] == $values['bw_disable_tagging'] )
{
$return['bw_disable_tagging'] = array( 'old' => $this->member->members_bitoptions['bw_disable_tagging'], 'new' => !$values['bw_disable_tagging'] );
$this->member->members_bitoptions['bw_disable_tagging'] = !$values['bw_disable_tagging'];
}
}
if ( !$this->member->group['gbw_disable_prefixes'] AND array_key_exists( 'bw_disable_prefixes', $values ) )
{
if ( $this->member->members_bitoptions['bw_disable_prefixes'] == $values['bw_disable_prefixes'] )
{
$return['bw_disable_prefixes'] = array( 'old' => $this->member->members_bitoptions['bw_disable_prefixes'], 'new' => !$values['bw_disable_prefixes'] );
$this->member->members_bitoptions['bw_disable_prefixes'] = !$values['bw_disable_prefixes'];
}
}
return $return;
}
/**
* What restrictions are active on the account?
*
* @return array
*/
public function activeRestrictions()
{
$return = array();
if ( !$this->member->group['gbw_disable_tagging'] and $this->member->members_bitoptions['bw_disable_tagging'] )
{
$return[] = 'restriction_no_tagging';
}
if ( !$this->member->group['bw_disable_prefixes'] and $this->member->members_bitoptions['bw_disable_prefixes'] )
{
$return[] = 'restriction_no_prefixes';
}
return $return;
}
/**
* Get details of a change to show on history
*
* @param array $changes Changes as set in save()
* @return array
*/
public static function changesForHistory( $changes )
{
$return = array();
foreach ( array( 'bw_disable_tagging', 'bw_disable_prefixes' ) as $k )
{
if ( isset( $changes[ $k ] ) )
{
$return[] = \IPS\Member::loggedIn()->language()->addToStack( 'history_restrictions_' . $k . '_' . intval( $changes[ $k ]['new'] ) );
}
}
return $return;
}
}
Whitespace-only changes.
@@ -1,97 +1,10 @@
<?php
/**
* @brief Member History Sync
* @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 02 Dec 2016
*/
// Removed
namespace IPS\core\extensions\core\MemberSync;
/* 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;
}
/**
* Member Sync
*/
class _History
{
/**
* Member is deleted
*
* @param $member \IPS\Member The member
* @return void
*/
public function onDelete( $member )
{
\IPS\Db::i()->delete( 'core_member_history', array( 'log_member=?', $member->member_id ) );
}
/**
* Email address is changed
*
* @param \IPS\Member $member The member
* @param string $new New email address
* @param string $old Old email address
* @return void
*/
public function onEmailChange( $member, $new, $old )
{
$member->logHistory( 'core', 'email_change', array( 'old' => $old, 'new' => $new ) );
}
/**
* Member is merged with another member
*
* @param \IPS\Member $member Member being kept
* @param \IPS\Member $member2 Member being removed
* @return void
*/
public function onMerge( $member, $member2 )
{
\IPS\Db::i()->update( 'core_member_history', array( 'log_member' => $member->member_id ), array( 'log_member=?', $member2->member_id ), array(), NULL, \IPS\Db::IGNORE );
\IPS\Db::i()->update( 'core_member_history', array( 'log_by' => $member->member_id ), array( 'log_by=?', $member2->member_id ), array(), NULL, \IPS\Db::IGNORE );
$member->logHistory( 'core', 'merged', array( 'old' => $member2->name ) );
}
/**
* Password is changed
*
* @param \IPS\Member $member The member
* @param string $new New password
* @return void
*/
public function onPassChange( $member, $new )
{
$member->logHistory( 'core', 'password_change', array( 'password' => '' ) );
}
/**
* Member is flagged as spammer
*
* @param $member \IPS\Member The member
* @return void
*/
public function onSetAsSpammer( $member )
{
$member->logHistory( 'core', 'spammer', array( 'set' => TRUE ) );
}
/**
* Member is unflagged as spammer
*
* @param $member \IPS\Member The member
* @return void
*/
public function onUnSetAsSpammer( $member )
{
$member->logHistory( 'core', 'spammer', array( 'set' => FALSE ) );
}
public function __construct() { }
}
@@ -30,7 +30,7 @@ class _System
* @return void
*/
public function onMerge( $member, $member2 )
{
{
/* Standard stuff */
foreach ( array(
array( 'core_admin_logs', 'member_id' ),
@@ -45,6 +45,8 @@ class _System
array( 'core_ignored_users', 'ignore_owner_id' ),
array( 'core_ignored_users', 'ignore_ignore_id' ),
array( 'core_incoming_emails', 'rule_added_by' ),
array( 'core_member_history', 'log_member' ),
array( 'core_member_history', 'log_by' ),
array( 'core_members_warn_logs', 'wl_member' ),
array( 'core_members_warn_logs', 'wl_moderator' ),
array( 'core_message_posts', 'msg_author_id' ),
@@ -53,6 +55,7 @@ class _System
array( 'core_moderator_logs', 'member_id' ),
array( 'core_notification_preferences', 'member_id' ),
array( 'core_notifications', 'member' ),
array( 'core_oauth_server_access_tokens', 'member_id' ),
array( 'core_polls', 'starter_id' ),
array( 'core_ratings', 'member' ),
array( 'core_rc_comments', 'comment_by' ),
@@ -67,6 +70,9 @@ class _System
array( 'core_tags', 'tag_member_id' ),
array( 'core_upgrade_history', 'upgrade_mid' ),
array( 'core_voters', 'member_id' ),
array( 'core_saved_charts', 'chart_member' ),
array( 'core_members_known_devices', 'member_id' ),
array( 'core_members_known_ip_addresses', 'member_id' ),
) as $toMerge
)
{
@@ -83,15 +89,22 @@ class _System
/* Delete duplicate stuff */
\IPS\Db::i()->delete( 'core_item_markers', array( 'item_member_id=?', $member2->member_id ) );
\IPS\Db::i()->delete( 'core_members_feature_seen', array( 'member_id=?', $member2->member_id ) );
\IPS\Db::i()->delete( 'core_security_answers', array( 'answer_member_id=?', $member2->member_id ) );
\IPS\Db::i()->delete( 'core_sessions', array( 'member_id=?', $member2->member_id ) );
\IPS\Db::i()->delete( 'core_sys_cp_sessions', array( 'session_member_id=?', $member2->member_id ) );
\IPS\Db::i()->delete( 'core_validating', array( 'member_id=?', $member2->member_id ) );
\IPS\Db::i()->query( 'DELETE row1 FROM ' . \IPS\Db::i()->prefix . 'core_reputation_index row1, ' . \IPS\Db::i()->prefix . 'core_reputation_index row2 WHERE row1.id > row2.id AND row1.member_id = row2.member_id AND row1.app = row2.app AND row1.type = row2.type AND row1.type_id = row2.type_id' );
\IPS\Db::i()->query( 'DELETE row1 FROM ' . \IPS\Db::i()->prefix . 'core_reputation_index row1, ' . \IPS\Db::i()->prefix . 'core_reputation_index row2 WHERE row1.id > row2.id AND row1.member_id = row2.member_id AND row1.app = row2.app AND row1.type = row2.type AND row1.type_id = row2.type_id AND row1.member_id IN(' . $member->member_id . ',' . $member2->member_id . ')' );
\IPS\Db::i()->delete( 'core_message_topic_user_map', array( 'map_user_id=?', $member2->member_id ) );
\IPS\Db::i()->query( 'DELETE row1 FROM ' . \IPS\Db::i()->prefix . 'core_message_topic_user_map row1, ' . \IPS\Db::i()->prefix . 'core_message_topic_user_map row2 WHERE row1.map_id > row2.map_id AND row1.map_user_id = row2.map_user_id AND row1.map_topic_id = row2.map_topic_id' );
\IPS\Db::i()->query( 'DELETE row1 FROM ' . \IPS\Db::i()->prefix . 'core_message_topic_user_map row1, ' . \IPS\Db::i()->prefix . 'core_message_topic_user_map row2 WHERE row1.map_id > row2.map_id AND row1.map_user_id = row2.map_user_id AND row1.map_topic_id = row2.map_topic_id AND row1.map_user_id IN(' . $member->member_id . ',' . $member2->member_id . ')' );
\IPS\Db::i()->delete( 'core_follow', array( 'follow_app=? AND follow_area=? AND follow_rel_id=follow_member_id', 'core', 'member') );
/* Update PM recipient counts */
foreach( \IPS\Db::i()->select( 'map_topic_id', 'core_message_topic_user_map', array( 'map_user_id=?', $member->member_id ) ) as $topicId )
{
\IPS\Db::i()->update( 'core_message_topics', array( 'mt_to_count' => \IPS\Db::i()->select( 'count(*)', 'core_message_topic_user_map', array( 'map_topic_id=?', $topicId ) ) ), array( 'mt_id=?', $topicId ) );
}
/* If one user is following both of the members involved in the merge, there would be duplicates */
$uniqueFollows = array();
$duplicateFollows = array();
@@ -135,7 +148,8 @@ class _System
}
/* Set warning level */
$member->warn_level += $member2->warn_level;
$member->warn_level += $member2->warn_level;
$member->pp_reputation_points += $member2->pp_reputation_points;
$member->save();
/* Recount notifications */
@@ -161,17 +175,23 @@ class _System
foreach( \IPS\Db::i()->select( 'reply_id', 'core_member_status_replies', array( 'reply_member_id=?', $member->member_id ) ) as $reply )
{
\IPS\Db::i()->delete( 'core_notifications', array( 'item_class=? AND item_id=?', 'IPS\\core\\Statuses\\Reply', $reply ) );
}
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_author=?', 'IPS\\core\\Statuses\\Reply', $member->member_id ) );
/* Remove item from deletion log */
\IPS\Db::i()->delete( 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id=?", "IPS\\core\\Statuses\\Reply", $reply ) );
}
\IPS\Content\Search\Index::i()->removeClassFromSearchIndex( 'IPS\core\Statuses\Reply', NULL, $member->member_id );
foreach( \IPS\Db::i()->select( 'status_id', 'core_member_status_updates', array( 'status_member_id=? OR status_author_id=?', $member->member_id, $member->member_id ) ) as $status )
{
\IPS\Db::i()->delete( 'core_notifications', array( 'item_class=? AND item_id=?', 'IPS\\core\\Statuses\\Status', $status ) );
/* Remove item from deletion log */
\IPS\Db::i()->delete( 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id=?", "IPS\\core\\Statuses\\Status", $status ) );
}
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_author=?', 'IPS\\core\\Statuses\\Status', $member->member_id ) );
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_container_id=?', 'IPS\\core\\Statuses\\Status', $member->member_id ) );
\IPS\Content\Search\Index::i()->removeClassFromSearchIndex( 'IPS\core\Statuses\Status', NULL, $member->member_id );
\IPS\Content\Search\Index::i()->removeClassFromSearchIndex( 'IPS\core\Statuses\Status', $member->member_id );
/* Generic deletes */
foreach ( array(
@@ -181,9 +201,11 @@ class _System
array( 'core_ignored_users', 'ignore_owner_id' ),
array( 'core_ignored_users', 'ignore_ignore_id' ),
array( 'core_item_markers', 'item_member_id' ),
array( 'core_member_history', 'log_member' ),
array( 'core_members_warn_logs', 'wl_member' ),
array( 'core_notification_preferences', 'member_id' ),
array( 'core_notifications', 'member' ),
array( 'core_oauth_server_access_tokens', 'member_id' ),
array( 'core_pfields_content', 'member_id' ),
array( 'core_ratings', 'member' ),
array( 'core_reputation_index', 'member_id' ),
@@ -197,6 +219,11 @@ class _System
array( 'core_member_status_updates', 'status_member_id' ),
array( 'core_member_status_updates', 'status_author_id' ),
array( 'core_member_status_replies', 'reply_member_id' ),
array( 'core_login_links', 'token_member' ),
array( 'core_members_feature_seen', 'member_id' ),
array( 'core_saved_charts', 'chart_member' ),
array( 'core_members_known_devices', 'member_id' ),
array( 'core_members_known_ip_addresses', 'member_id' ),
) as $toDelete )
{
\IPS\Db::i()->delete( $toDelete[0], array( $toDelete[1] . '=?', $member->member_id ) );
@@ -219,6 +246,8 @@ class _System
\IPS\Db::i()->delete( 'core_follow', array( 'follow_app=? AND follow_area=? AND follow_rel_id=?', 'core', 'member', $member->member_id ) );
\IPS\Db::i()->delete( 'core_moderators', array( 'id=? AND type=?', $member->member_id, 'm' ) );
unset ( \IPS\Data\Store::i()->moderators );
/* Remove the group from the staff directory */
foreach ( \IPS\core\StaffDirectory\User::roots() as $staff )
@@ -1,365 +1,10 @@
<?php
/**
* @brief Member Sync for IPS Connect actions
* @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 27 Nov 2013
*/
// Removed
namespace IPS\core\extensions\core\MemberSync;
/* 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;
}
/**
* Member Sync for IPS Connect actions
*/
class _ipsconnect
{
/**
* Member has logged on
*
* @param \IPS\Member $member Member that logged in
* @param \IPS\Http\Url $redirectUrl The URL to send the user back to
* @return void
*/
public function onLogin( $member, $returnUrl )
{
if( !$this->_checkStatus( $member ) )
{
return;
}
/* We will redirect to our normal end point to handle this */
\IPS\Output::i()->redirect(
\IPS\Http\Url::external( \IPS\Settings::i()->base_url . '/applications/core/interface/ipsconnect/ipsconnect.php' )->setQueryString(
array(
'do' => 'crossLogin',
'key' => md5( ( \IPS\CONNECT_MASTER_KEY ?: md5( md5( \IPS\Settings::i()->sql_user . \IPS\Settings::i()->sql_pass ) . \IPS\Settings::i()->board_start ) ) . $member->member_id ),
'url' => \IPS\Settings::i()->base_url . '/applications/core/interface/ipsconnect/ipsconnect.php',
'id' => $member->member_id,
'returnTo' => (string) $returnUrl
)
)
);
}
/**
* Member has logged out
*
* @param \IPS\Member $member Member that logged out
* @param \IPS\Http\Url $redirectUrl The URL to send the user back to
* @return void
*/
public function onLogout( $member, $returnUrl )
{
if( !$this->_checkStatus( $member ) )
{
return;
}
\IPS\Output::i()->redirect(
\IPS\Http\Url::external( \IPS\Settings::i()->base_url . '/applications/core/interface/ipsconnect/ipsconnect.php' )->setQueryString(
array(
'do' => 'logout',
'key' => md5( ( \IPS\CONNECT_MASTER_KEY ?: md5( md5( \IPS\Settings::i()->sql_user . \IPS\Settings::i()->sql_pass ) . \IPS\Settings::i()->board_start ) ) . $member->member_id ),
'url' => \IPS\Settings::i()->base_url . '/applications/core/interface/ipsconnect/ipsconnect.php',
'id' => $member->member_id,
'returnTo' => (string) $returnUrl
)
)
);
}
/**
* Member has validated
*
* @param \IPS\Member $member Member validated
* @return void
*/
public function onValidate( $member )
{
if( !$this->_checkStatus( $member ) )
{
return;
}
$this->_propagateRequest( array(
'do' => 'validate',
'key' => '_SERVER_ID_',
'id' => $member->member_id,
) );
}
/**
* Member is merged with another member
*
* @param \IPS\Member $member Member being kept
* @param \IPS\Member $member2 Member being removed
* @return void
*/
public function onMerge( $member, $member2 )
{
if( !$this->_checkStatus( $member ) )
{
return;
}
$this->_propagateRequest( array(
'do' => 'merge',
'key' => '_SERVER_ID_',
'id' => $member->member_id,
'remove' => $member2->member_id,
) );
}
/**
* Member is deleted
*
* @param $member \IPS\Member Member being deleted
* @return void
*/
public function onDelete( $member )
{
if( !$this->_checkStatus( $member ) )
{
return;
}
$this->_propagateRequest( array(
'do' => 'delete',
'key' => '_SERVER_ID_',
'id' => $member->member_id,
) );
}
/**
* Member account has been created
*
* @param $member \IPS\Member New member account
* @return void
*/
public function onCreateAccount( $member )
{
if( !$this->_checkStatus( $member ) )
{
return;
}
/* We can only create accounts on the master if we have all the details */
if( !$member->name OR !$member->email OR !$member->members_pass_hash OR !$member->members_pass_salt )
{
return;
}
$this->_propagateRequest( array(
'do' => 'register',
'key' => '_SERVER_',
'id' => $member->member_id,
'name' => $member->name,
'email' => $member->email,
'pass_hash' => $member->members_pass_hash,
'pass_salt' => $member->members_pass_salt,
'revalidateUrl' => ( $member->members_bitoptions['validating'] == TRUE ) ? (string) \IPS\Http\Url::internal( "", 'front' ) : ''
) );
}
/**
* Member account has been updated
*
* @param $member \IPS\Member Member updating profile
* @param $changes array The changes
* @return void
*/
public function onProfileUpdate( $member, $changes )
{
if( !$this->_checkStatus( $member ) )
{
return;
}
if( !count( $changes ) )
{
return;
}
/* We need to figure out what has changed first - not all changes are propagated */
/* Email address */
if( isset( $changes['email'] ) )
{
$this->_propagateRequest( array(
'do' => 'changeEmail',
'key' => '_SERVER_ID_',
'id' => $member->member_id,
'email' => $changes['email']
) );
}
/* Username */
if( \IPS\CONNECT_NOSYNC_NAMES !== TRUE )
{
if( isset( $changes['name'] ) )
{
$this->_propagateRequest( array(
'do' => 'changeName',
'key' => '_SERVER_ID_',
'id' => $member->member_id,
'name' => $changes['name']
) );
}
}
/* Password */
if( isset( $changes['members_pass_hash'] ) )
{
$this->_propagateRequest( array(
'do' => 'changePassword',
'key' => '_SERVER_ID_',
'id' => $member->member_id,
'pass_hash' => $changes['members_pass_hash'],
'pass_salt' => $changes['members_pass_salt'],
) );
}
/* Ban status */
if( isset( $changes['temp_ban'] ) )
{
$this->_propagateRequest( array(
'do' => 'ban',
'key' => '_SERVER_ID_',
'id' => $member->member_id,
'status' => ( $changes['temp_ban'] == -1 ) ? 1 : 0,
) );
}
}
/**
* Password is changed
*
* @param \IPS\Member $member The member
* @param string $new New password
* @return void
*/
public function onPassChange( $member, $new )
{
if ( !$this->_checkStatus( $member ) )
{
return;
}
$this->_propagateRequest( array(
'do' => 'changePassword',
'key' => '_SERVER_ID_',
'id' => $member->member_id,
'pass_hash' => $member->members_pass_hash,
'pass_salt' => $member->members_pass_salt
) );
}
/**
* We only need to push the requests to our slaves if we are the master and if the request
* was not triggered by a connect call.
*
* @param $member \IPS\Member|NULL Optional member account to check
* @return bool
*/
protected function _checkStatus( $member = NULL )
{
if( !\IPS\Settings::i()->connect_slaves )
{
return FALSE;
}
else
{
/* Was the change triggered by a connect call already? */
if( mb_strrpos( $_SERVER['SCRIPT_FILENAME'], 'ipsconnect.php' ) !== FALSE )
{
return FALSE;
}
/* Are we also testing a member account? */
if( $member !== NULL )
{
foreach( \IPS\Login::handlers( TRUE ) as $key => $handler )
{
if( $key == 'Internal' )
{
if( !$handler->canProcess( $member ) )
{
return FALSE;
}
break;
}
}
}
return TRUE;
}
}
/**
* Propagate a request to all registered slaves
*
* @param array $queryString The query string to send
* @return void
*/
protected function _propagateRequest( $queryString )
{
$select = \IPS\Db::i()->select( '*', 'core_ipsconnect_slaves', array( 'slave_enabled=?', 1 ) );
if( $select->count() )
{
foreach( $select as $row )
{
$key = '';
if( isset( $queryString['key'] ) )
{
if( $queryString['key'] == '_SERVER_' )
{
$key = $row['slave_key'];
}
else if( $queryString['key'] == '_SERVER_ID_' )
{
$key = md5( $row['slave_key'] . $queryString['id'] );
}
}
try
{
$response = \IPS\Http\Url::external( $row['slave_url'] )
->setQueryString( array_merge( $queryString, array( 'slaveCall' => 1, 'key' => $key ?: ( ( isset( $queryString['key'] ) ) ? $queryString['key'] : '' ) ) ) )
->request()
->get()
->decodeJson();
/* If this is a registered slave but the slave is telling us Connect is disabled, remove it */
if( isset( $response['status'] ) AND $response['status'] == 'DISABLED' )
{
\IPS\Db::i()->update( 'core_ipsconnect_slaves', array( 'slave_enabled' => 0 ), array( 'slave_id=?', $row['slave_id'] ) );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => \IPS\Db::i()->select( 'COUNT(*)', 'core_ipsconnect_slaves', array( 'slave_enabled=?', 1 ) )->first() ), array( 'conf_key=?', 'connect_slaves' ) );
unset( \IPS\Data\Store::i()->settings );
}
else if( !isset( $response['status'] ) OR $response['status'] != 'SUCCESS' )
{
throw new \RuntimeException( $response['status'] );
}
}
catch( \RuntimeException $e )
{
\IPS\Db::i()->insert( 'core_ipsconnect_queue', array( 'slave_id' => $row['slave_id'], 'request_url' => http_build_query( array_merge( $queryString, array( 'slaveCall' => 1, 'key' => $key ?: ( ( isset( $queryString['key'] ) ) ? $queryString['key'] : '' ) ) ) ) ) );
/* Make sure task is enabled */
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), "`key`='connect'" );
}
}
}
}
public function __construct() { }
}
@@ -5,7 +5,6 @@
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 04 Dec 2016
*/
@@ -82,6 +82,8 @@ class _Deleted
*/
public function modaction()
{
\IPS\Session::i()->csrfCheck();
$ids = array();
foreach( \IPS\Request::i()->moderate AS $id => $status )
{
@@ -92,89 +94,92 @@ class _Deleted
{
$class = $log->content_class;
$content = $class::load( $log->content_id );
switch( \IPS\Request::i()->modaction )
if ( $content->canView() )
{
case 'restore':
if ( isset( $class::$databaseColumnMap['hidden'] ) )
{
$column = $class::$databaseColumnMap['hidden'];
$content->$column = 0;
}
else if ( isset( $class::$databaseColumnMap['approved'] ) )
{
$column = $class::$databaseColumnMap['approved'];
$content->$column = 1;
}
\IPS\Session::i()->modLog( 'modlog__action_restore', array(
$content::$title => FALSE,
$content->url()->__toString() => FALSE
) );
try
{
if ( $content->container() )
switch( \IPS\Request::i()->modaction )
{
case 'restore':
if ( isset( $class::$databaseColumnMap['hidden'] ) )
{
if ( $content->container()->_comments !== NULL )
{
if ( $content instanceof \IPS\Content\Item )
{
$content->container()->_comments = ( $content->container()->_comments + $content->mapped('num_comments') );
}
else
{
$content->container()->_comments = $content->container()->_comments + 1;
}
}
if ( $content->container()->_unapprovedComments !== NULL )
{
if ( $content instanceof \IPS\Content\Item )
{
$content->container()->_unapprovedComments = ( $content->container()->_unapprovedComments + $content->mapped('unapproved_comments') );
}
}
$content->container()->setLastComment();
$content->container()->save();
$column = $class::$databaseColumnMap['hidden'];
$content->$column = 0;
}
}
catch( \BadMethodCallException $e ) {}
$content->save();
$log->delete();
break;
case 'restore_as_hidden':
if ( isset( $class::$databaseColumnMap['hidden'] ) )
{
$column = $class::$databaseColumnMap['hidden'];
}
else if ( isset( $class::$databaseColumnMap['approved'] ) )
{
$column = $class::$databaseColumnMap['approved'];
}
$content->$column = -1;
\IPS\Session::i()->modLog( 'modlog__action_restore_hidden', array(
$content::$title => FALSE,
$content->url()->__toString() => FALSE
) );
$content->save();
$log->delete();
break;
case 'delete':
\IPS\Session::i()->modLog( 'modlog__action_delete_perm', array(
$content::$title => FALSE,
$content->url()->__toString() => FALSE
) );
$content->delete();
$log->delete();
break;
else if ( isset( $class::$databaseColumnMap['approved'] ) )
{
$column = $class::$databaseColumnMap['approved'];
$content->$column = 1;
}
\IPS\Session::i()->modLog( 'modlog__action_restore', array(
$content::$title => FALSE,
$content->url()->__toString() => FALSE
) );
try
{
if ( $content->container() )
{
if ( $content->container()->_comments !== NULL )
{
if ( $content instanceof \IPS\Content\Item )
{
$content->container()->_comments = ( $content->container()->_comments + $content->mapped('num_comments') );
}
else
{
$content->container()->_comments = $content->container()->_comments + 1;
}
}
if ( $content->container()->_unapprovedComments !== NULL )
{
if ( $content instanceof \IPS\Content\Item )
{
$content->container()->_unapprovedComments = ( $content->container()->_unapprovedComments + $content->mapped('unapproved_comments') );
}
}
$content->container()->setLastComment();
$content->container()->save();
}
}
catch( \BadMethodCallException $e ) {}
$content->save();
$log->delete();
break;
case 'restore_as_hidden':
if ( isset( $class::$databaseColumnMap['hidden'] ) )
{
$column = $class::$databaseColumnMap['hidden'];
}
else if ( isset( $class::$databaseColumnMap['approved'] ) )
{
$column = $class::$databaseColumnMap['approved'];
}
$content->$column = -1;
\IPS\Session::i()->modLog( 'modlog__action_restore_hidden', array(
$content::$title => FALSE,
$content->url()->__toString() => FALSE
) );
$content->save();
$log->delete();
break;
case 'delete':
\IPS\Session::i()->modLog( 'modlog__action_delete_perm', array(
$content::$title => FALSE,
$content->url()->__toString() => FALSE
) );
$content->delete();
$log->delete();
break;
}
}
}
@@ -62,22 +62,21 @@ class _IPTools extends \IPS\core\modules\admin\members\ip
if ( isset( \IPS\Request::i()->area ) )
{
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack( 'ipAddresses__' . \IPS\Request::i()->area ) );
$exploded = explode( '_', \IPS\Request::i()->area );
$extensions = \IPS\Application::load( $exploded[0] )->extensions( 'core', 'IpAddresses' );
$extensions = \IPS\Application::appIsEnabled( $exploded[0] ) ? \IPS\Application::load( $exploded[0] )->extensions( 'core', 'IpAddresses' ) : array();
/* If the extension no longer exists (application uninstalled) then fall back */
if( isset( $extensions[ mb_substr( \IPS\Request::i()->area, mb_strlen( $exploded[0] ) + 1 ) ] ) and ( !method_exists( $extensions[ mb_substr( \IPS\Request::i()->area, mb_strlen( $exploded[0] ) + 1 ) ], 'supportedInModCp' ) or $extensions[ mb_substr( \IPS\Request::i()->area, mb_strlen( $exploded[0] ) + 1 ) ]->supportedInModCp() ) )
{
$class = $extensions[ mb_substr( \IPS\Request::i()->area, mb_strlen( $exploded[0] ) + 1 ) ];
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack( 'ipAddresses__' . \IPS\Request::i()->area ) );
\IPS\Output::i()->output = $class->findByIp( str_replace( '*', '%', $ip ), $url->setQueryString( 'area', \IPS\Request::i()->area ) );
}
else
{
\IPS\Output::i()->error( 'node_error', '2C250/2', 404, '' );
}
\IPS\Output::i()->output = $class->findByIp( str_replace( '*', '%', $ip ), $url->setQueryString( 'area', \IPS\Request::i()->area ) );
}
else
{
@@ -225,13 +225,13 @@ class _Reports extends \IPS\Content\Controller
catch ( \UnderflowException $e ) {}
/* Display */
if ( \IPS\Request::i()->isAjax() and !isset( \IPS\Request::i()->_contentReply ) and !isset( \IPS\Request::i()->getUploader ) AND !isset( \IPS\Request::i()->page ) )
if ( \IPS\Request::i()->isAjax() and !isset( \IPS\Request::i()->_contentReply ) and !isset( \IPS\Request::i()->getUploader ) AND !isset( \IPS\Request::i()->page ) AND !isset( \IPS\Request::i()->_previewField ) )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'modcp' )->reportPanel( $report, $comment, $ref );
}
else
{
$sprintf = $item ? htmlspecialchars( $item->mapped('title'), \IPS\HTMLENTITIES | ENT_QUOTES, 'UTF-8', FALSE ) : \IPS\Member::loggedIn()->language()->addToStack('content_deleted');
$sprintf = $item ? htmlspecialchars( $item->mapped('title'), ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) : \IPS\Member::loggedIn()->language()->addToStack('content_deleted');
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'modcp_reports_view', FALSE, array( 'sprintf' => array( $sprintf ) ) );
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=modcp&controller=modcp&tab=reports', 'front', 'modcp_reports' ), \IPS\Member::loggedIn()->language()->addToStack( 'modcp_reports' ) );
\IPS\Output::i()->breadcrumb[] = array( NULL, $item ? $item->mapped('title') : \IPS\Member::loggedIn()->language()->addToStack( 'content_deleted' ) );
@@ -261,4 +261,37 @@ class _Reports extends \IPS\Content\Controller
\IPS\Output::i()->redirect( $url );
}
/**
* Return a comment URL
*
* @return \IPS\Http\Url
*/
public function findComment()
{
try
{
$report = \IPS\core\Reports\Report::loadAndCheckPerms( \IPS\Request::i()->id );
$comment = \IPS\core\Reports\Comment::load( \IPS\Request::i()->comment );
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C139/4', 404, '' );
}
$url = $report->url()->setQueryString( 'activeTab', 'comments' );
$idColumn = \IPS\core\Reports\Report::$databaseColumnId;
$commentIdColumn = \IPS\core\Reports\Comment::$databaseColumnId;
$position = \IPS\Db::i()->select( 'COUNT(*)', 'core_rc_comments', array( "rid=? AND id<=?", $report->$idColumn, $comment->$commentIdColumn ) )->first();
$page = ceil( $position / $report::getCommentsPerPage() );
if ( $page != 1 )
{
$url = $url->setQueryString( 'page', $page );
}
\IPS\Output::i()->redirect( $url->setFragment( 'comment-' . $comment->$commentIdColumn ) );
}
}
@@ -47,7 +47,8 @@ class _Unapproved
$showSplash = FALSE;
/* If the user wants to skip this splash next time, set a cookie we can look for */
if ( isset( \IPS\Request::i()->skipnext ) ){
if ( isset( \IPS\Request::i()->skipnext ) )
{
\IPS\Request::i()->setCookie( 'ipsApprovalQueueSplash', true, \IPS\DateTime::ts( time() )->add( new \DateInterval( 'P1Y' ) ) );
}
@@ -198,12 +199,7 @@ class _Unapproved
if ( \IPS\Request::i()->isAjax() )
{
$approvalQueueCount = \IPS\Content\Search\Query::init()->setHiddenFilter( \IPS\Content\Search\Query::HIDDEN_UNAPPROVED )->count();
if ( \IPS\Settings::i()->clubs and \IPS\Settings::i()->clubs_require_approval and \IPS\Member::loggedIn()->modPermission('can_access_all_clubs') )
{
$approvalQueueCount += \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs', 'approved=0' )->first();
}
return \IPS\Output::i()->json( array( 'html' => $return, 'count' => $approvalQueueCount ) );
return \IPS\Output::i()->json( array( 'html' => $return, 'count' => $this->getApprovalQueueCount( TRUE ) ) );
}
else
{
@@ -237,12 +233,7 @@ class _Unapproved
if ( \IPS\Request::i()->isAjax() )
{
$approvalQueueCount = \IPS\Content\Search\Query::init()->setHiddenFilter( \IPS\Content\Search\Query::HIDDEN_UNAPPROVED )->count();
if ( \IPS\Settings::i()->clubs and \IPS\Settings::i()->clubs_require_approval and \IPS\Member::loggedIn()->modPermission('can_access_all_clubs') )
{
$approvalQueueCount += \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs', 'approved=0' )->first();
}
return \IPS\Output::i()->json( array( 'html' => $return, 'count' => $approvalQueueCount ) );
return \IPS\Output::i()->json( array( 'html' => $return, 'count' => $this->getApprovalQueueCount( TRUE ) ) );
}
else
{
@@ -267,6 +258,68 @@ class _Unapproved
return \IPS\Theme::i()->getTemplate('modcp')->approvalQueueEmpty();
}
}
/**
* Get the approval queue count
*
* @param bool $bypassCache Ignore cache and refetch the count
* @return int
*/
public function getApprovalQueueCount( $bypassCache = FALSE )
{
try
{
if( $bypassCache === TRUE )
{
throw new \OutOfRangeException;
}
$approvalQueueCount = \IPS\Data\Cache::i()->getWithExpire( 'modCpApprovalQueueCount_' . \IPS\Member::loggedIn()->member_id, TRUE );
}
catch ( \OutOfRangeException $e )
{
$approvalQueueCount = 0;
foreach ( \IPS\Content::routedClasses( TRUE ) as $class )
{
if ( in_array( 'IPS\Content\Hideable', class_implements( $class ) ) )
{
$where = array();
if ( isset( $class::$databaseColumnMap['hidden'] ) )
{
$where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['hidden'] . '=1' );
}
elseif ( isset( $class::$databaseColumnMap['approved'] ) )
{
$where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['approved'] . '=0' );
}
else
{
continue;
}
if ( isset( $_SESSION['ipsApprovalQueueSkip'][ md5( $class ) ] ) )
{
$where[] = '!' . \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnId, $_SESSION['ipsApprovalQueueSkip'][ md5( $class ) ] );
}
if ( is_subclass_of( $class, 'IPS\Content\Comment' ) AND $class::commentWhere() !== NULL )
{
$where[] = $class::commentWhere();
}
$approvalQueueCount += $class::getItemsWithPermission( $where, NULL, 1, 'read', \IPS\Content\Hideable::FILTER_AUTOMATIC, 0, NULL, FALSE, FALSE, FALSE, TRUE );
}
}
if ( \IPS\Settings::i()->clubs and \IPS\Settings::i()->clubs_require_approval and \IPS\Member::loggedIn()->modPermission('can_access_all_clubs') )
{
$approvalQueueCount += \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs', 'approved=0' )->first();
}
\IPS\Data\Cache::i()->storeWithExpire( 'modCpApprovalQueueCount_' . \IPS\Member::loggedIn()->member_id, $approvalQueueCount, \IPS\DateTime::create()->add( new \DateInterval( 'PT15M' ) ), TRUE );
}
return $approvalQueueCount;
}
}
@@ -93,7 +93,11 @@ class _Content
*/
public function onChange( $moderator, $changed )
{
/* Rebuild the create menu cache if the announcement permission was changed */
if ( $changed == 'can_manage_announcements' )
{
\IPS\Member::clearCreateMenu();
}
}
/**
@@ -49,6 +49,10 @@ class _ContentGenerator extends \IPS\Content\ExtensionGenerator
{
/* Init */
$class = $this->class;
if ( !$class::$canBeModeratedFromFrontend )
{
return array();
}
$return = array();
/* Node selector */
@@ -42,6 +42,11 @@ class _Clubs
$return['club_request'] = array( 'default' => array( 'inline' ), 'disabled' => array() );
$return['club_join'] = array( 'default' => array( 'inline' ), 'disabled' => array() );
}
if ( $member === NULL or $member->modPermission( 'can_access_all_clubs' ) )
{
$return['unapproved_club'] = array( 'default' => array( 'inline' ), 'disabled' => array(), 'icon' => 'lock' );
}
}
return $return;
@@ -147,9 +152,16 @@ class _Clubs
$requests = \IPS\Db::i()->select( array( 'member_id', 'joined' ), 'core_clubs_memberships', array( 'club_id=? AND joined>=? AND joined<? AND status=?', $club->id, $notification->sent_time->getTimestamp()-1, $between, \IPS\Member\Club::STATUS_REQUESTED ), 'joined desc', NULL )->setValueField('member_id');
$names = array();
$names = array();
$first = NULL;
foreach( $requests AS $member )
{
if( $first === NULL )
{
$first = $member;
}
if ( count( $names ) > 2 )
{
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'x_others', FALSE, array( 'pluralize' => array( count( $requests ) - 3 ) ) );
@@ -157,20 +169,17 @@ class _Clubs
}
$names[] = \IPS\Member::load( $member )->name;
}
try
{
$first = $requests->first();
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__club_request', FALSE, array( 'pluralize' => array( count( $requests ) ), 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $names ), $club->name ) ) ),
'url' => $club->url()->setQueryString( array( 'do' => 'members', 'filter' => \IPS\Member\Club::STATUS_REQUESTED ) ),
'author' => \IPS\Member::load( $first )
);
}
catch( \UnderflowException $ex )
if( $first === NULL )
{
throw new \OutOfRangeException;
}
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__club_request', FALSE, array( 'pluralize' => array( count( $requests ) ), 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $names ), $club->name ) ) ),
'url' => $club->url()->setQueryString( array( 'do' => 'members', 'filter' => \IPS\Member\Club::STATUS_REQUESTED ) ),
'author' => \IPS\Member::load( $first )
);
}
/**
@@ -205,11 +214,18 @@ class _Clubs
}
catch( \UnderflowException $e ) {}
$requests = \IPS\Db::i()->select( array( 'member_id', 'joined' ), 'core_clubs_memberships', array( 'club_id=? AND joined>=? AND joined<? AND ( status=? OR status=? )', $club->id, $notification->sent_time->getTimestamp()-1, $between, \IPS\Member\Club::STATUS_MEMBER, \IPS\Member\Club::STATUS_MODERATOR, \IPS\Member\Club::STATUS_LEADER ), 'joined desc' )->setValueField('member_id');
$requests = \IPS\Db::i()->select( array( 'member_id', 'joined' ), 'core_clubs_memberships', array( 'club_id=? AND joined>=? AND joined<? AND ( status=? OR status=? )', $club->id, $notification->sent_time->getTimestamp()-1, $between, \IPS\Member\Club::STATUS_MEMBER, \IPS\Member\Club::STATUS_MODERATOR, \IPS\Member\Club::STATUS_LEADER, \IPS\Member\Club::STATUS_EXPIRED, \IPS\Member\Club::STATUS_EXPIRED_MODERATOR ), 'joined desc' )->setValueField('member_id');
$names = array();
$names = array();
$first = NULL;
foreach( $requests AS $member )
{
if( $first === NULL )
{
$first = $member;
}
if ( count( $names ) > 2 )
{
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'x_others', FALSE, array( 'pluralize' => array( count( $requests ) - 3 ) ) );
@@ -218,13 +234,48 @@ class _Clubs
$names[] = \IPS\Member::load( $member )->name;
}
if( $first === NULL )
{
throw new \OutOfRangeException;
}
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__club_join', FALSE, array( 'pluralize' => array( count( $requests ) ), 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $names ), $club->name ) ) ),
'url' => $club->url(),
'author' => \IPS\Member::load( $first )
);
}
/**
* Parse notification: unapproved_club
*
* @param \IPS\Notification\Inline $notification The notification
* @return array|NULL
* @code
return array(
'title' => "Mark has replied to A Topic", // The notification title
'url' => \IPS\Http\Url::internal( ... ), // The URL the notification should link to
'content' => "Lorem ipsum dolar sit", // [Optional] Any appropriate content. Do not format this like an email where the text
// explains what the notification is about - just include any appropriate content.
// For example, if the notification is about a post, set this as the body of the post.
'author' => \IPS\Member::load( 1 ), // [Optional] The user whose photo should be displayed for this notification
);
* @endcode
*/
public function parse_unapproved_club( $notification )
{
$club = $notification->item;
if ( !\IPS\Settings::i()->clubs or !$club or $club->memberStatus )
{
throw new \OutOfRangeException;
}
try
{
$first = $requests->first();
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__club_join', FALSE, array( 'pluralize' => array( count( $requests ) ), 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $names ), $club->name ) ) ),
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_club_unapproved', FALSE, array( 'sprintf' => array( $club->owner->name, $club->name ) ) ),
'url' => $club->url(),
'author' => \IPS\Member::load( $first )
'author' => $club->owner,
);
}
catch( \UnderflowException $ex )
@@ -232,5 +283,4 @@ class _Clubs
throw new \OutOfRangeException;
}
}
}
@@ -52,6 +52,7 @@ class _Content
if ( $member === NULL or $member->modPermission( 'can_view_reports' ) )
{
$return['report_center'] = array( 'default' => array( 'inline', 'email' ), 'disabled' => array(), 'icon' => 'lock' );
$return['automatic_moderation'] = array( 'default' => array( 'inline', 'email' ), 'disabled' => array(), 'icon' => 'lock' );
}
if ( $member === NULL or $member->modPermission( 'can_view_hidden_content' ) )
@@ -410,6 +411,40 @@ class _Content
);
}
/**
* Parse notification: automatic_moderation
*
* @param \IPS\Notification\Inline $notification The notification
* @return array
* @code
return array(
'title' => "Mark has replied to A Topic", // The notification title
'url' => \IPS\Http\Url::internal( ... ), // The URL the notification should link to
'content' => "Lorem ipsum dolar sit", // [Optional] Any appropriate content. Do not format this like an email where the text
// explains what the notification is about - just include any appropriate content.
// For example, if the notification is about a post, set this as the body of the post.
'author' => \IPS\Member::load( 1 ), // [Optional] The user whose photo should be displayed for this notification
);
* @endcode
*/
public function parse_automatic_moderation( $notification )
{
if ( !$notification->item_sub )
{
throw new \OutOfRangeException;
}
$reported = $notification->item_sub;
$item = ( $reported instanceof \IPS\Content\Item ) ? $reported : $reported->item();
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__automatic_moderation', FALSE, array( 'sprintf' => array( mb_strtolower( $reported->indefiniteArticle() ), $item->mapped('title' ) ) ) ),
'url' => $notification->item->url(),
'content' => NULL,
'author' => $notification->item->author(),
);
}
/**
* Parse notification: unapproved_content
*
@@ -502,10 +537,11 @@ class _Content
{
$quoters = array( $comment->author()->name );
}
$quoters = \IPS\Member::loggedIn()->language()->formatList( $quoters );
$count = count( $quoters );
$quoters = \IPS\Member::loggedIn()->language()->formatList( $quoters );
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_mention', FALSE, array( 'pluralize' => array( count( $quoters ) ), 'sprintf' => array( $quoters, mb_strtolower( $item->indefiniteArticle() ), $item->mapped('title') ) ) ),
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_mention', FALSE, array( 'pluralize' => array( $count ), 'sprintf' => array( $quoters, mb_strtolower( $item->indefiniteArticle() ), $item->mapped('title') ) ) ),
'url' => $comment instanceof \IPS\Content\Item ? $item->url() : $comment->url('find'),
'content' => $comment->truncated(),
'author' => $comment->author(),
@@ -563,10 +599,11 @@ class _Content
{
$quoters = array( $comment->author()->name );
}
$quoters = \IPS\Member::loggedIn()->language()->formatList( $quoters );
$count = count( $quoters );
$quoters = \IPS\Member::loggedIn()->language()->formatList( $quoters );
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_quote', FALSE, array( 'pluralize' => array( count( $quoters ) ), 'sprintf' => array( $quoters, mb_strtolower( $item->indefiniteArticle() ), $item->mapped('title') ) ) ),
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_quote', FALSE, array( 'pluralize' => array( $count ), 'sprintf' => array( $quoters, mb_strtolower( $item->indefiniteArticle() ), $item->mapped('title') ) ) ),
'url' => $comment instanceof \IPS\Content\Item ? $item->url() : $comment->url('find'),
'content' => $comment->truncated(),
'author' => $comment->author(),
@@ -592,7 +629,6 @@ class _Content
*/
public function parse_new_likes( $notification )
{
$comment = $notification->item;
if ( !$comment )
@@ -613,9 +649,15 @@ class _Content
$likers = \IPS\Db::i()->select( 'DISTINCT member_id, rep_date', 'core_reputation_index', array( 'app=? AND type=? AND rep_date>=? AND rep_date<? AND type_id=?', $comment::$application, $comment::reactionType(), $notification->sent_time->getTimestamp(), $between, $comment->$idColumn ), 'rep_date desc' );
$names = array();
$names = array();
$first = array();
foreach( $likers AS $member )
{
if( empty( $first ) )
{
$first = $member;
}
if ( count( $names ) > 2 )
{
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'x_others', FALSE, array( 'pluralize' => array( count( $likers ) - 3 ) ) );
@@ -623,21 +665,18 @@ class _Content
}
$names[] = \IPS\Member::load( $member['member_id'] )->name;
}
try
{
$first = $likers->first();
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( \IPS\Content\Reaction::isLikeMode() ? 'notification__new_likes' : 'notification__new_react', FALSE, array( 'pluralize' => array( count( $likers ) ), 'sprintf' => array( ( \IPS\Member::loggedIn()->group['gbw_view_reps'] ) ? \IPS\Member::loggedIn()->language()->formatList( $names ) : \IPS\Member::loggedIn()->language()->pluralize( \IPS\Member::loggedIn()->language()->get( 'notifications_user_count' ), array( count( $likers ) ) ), mb_strtolower( $comment->indefiniteArticle() ) ) ) ) . ' ' . $item->mapped('title'),
'url' => ( $comment instanceof \IPS\Content\Comment ) ? $comment->url('find') : $comment->url(),
'content' => $comment->truncated(),
'author' => ( \IPS\Member::loggedIn()->group['gbw_view_reps'] ) ? \IPS\Member::load( $first['member_id'] ) : new \IPS\Member
);
}
catch( \UnderflowException $ex )
if( empty( $first ) )
{
throw new \OutOfRangeException;
}
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( \IPS\Content\Reaction::isLikeMode() ? 'notification__new_likes' : 'notification__new_react', FALSE, array( 'pluralize' => array( count( $likers ) ), 'sprintf' => array( ( \IPS\Member::loggedIn()->group['gbw_view_reps'] ) ? \IPS\Member::loggedIn()->language()->formatList( $names ) : \IPS\Member::loggedIn()->language()->pluralize( \IPS\Member::loggedIn()->language()->get( \IPS\Content\Reaction::isLikeMode() ? 'notifications_user_count_like' : 'notifications_user_count_react' ), array( count( $likers ) ) ), mb_strtolower( $comment->indefiniteArticle() ) ) ) ) . ' ' . $item->mapped('title'),
'url' => ( $comment instanceof \IPS\Content\Comment ) ? $comment->url('find') : $comment->url(),
'content' => $comment->truncated(),
'author' => ( \IPS\Member::loggedIn()->group['gbw_view_reps'] ) ? \IPS\Member::load( $first['member_id'] ) : new \IPS\Member
);
}
}
@@ -108,7 +108,7 @@ class _Messenger
return array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__private_message_added', FALSE, array( 'sprintf' => array( $item->author()->name ) ) ),
'url' => $item->item()->url(),
'url' => $item->url(),
'content' => $item->content(),
'author' => \IPS\Member::load( $item->author()->member_id ),
);
@@ -93,7 +93,6 @@ class _Profile
*/
public function parse_profile_comment( $notification )
{
$item = $notification->item;
if ( !$item )
@@ -127,7 +126,6 @@ class _Profile
*/
public function parse_new_status( $notification )
{
$item = $notification->item;
if ( !$item )
@@ -161,7 +159,6 @@ class _Profile
*/
public function parse_profile_reply( $notification )
{
$item = $notification->item;
if ( !$item )
@@ -32,18 +32,15 @@ class _Core
public static function actions()
{
$return = array( 'basic_profile' => 'complete_profile_basic_profile' );
/* Social Integration */
foreach( array( 'facebook', 'twitter', 'linkedin', 'google', 'live' ) AS $service )
foreach ( \IPS\Login::methods() as $method )
{
$handler = \IPS\Login::getHandler( ucfirst( $service ) );
if ( $handler->_enabled )
if ( $method->showInUcp() )
{
$return['social_login'] = 'complete_profile_social_login';
break;
}
}
return $return;
}
@@ -54,7 +51,6 @@ class _Core
*/
public static function subActions()
{
/* Basic stuff */
$return['basic_profile'] = array(
'photo' => 'complete_profile_photo',
@@ -69,12 +65,11 @@ class _Core
}
/* Social Integration */
foreach( array( 'facebook', 'twitter', 'linkedin', 'google', 'live' ) AS $service )
foreach ( \IPS\Login::methods() as $method )
{
$handler = \IPS\Login::getHandler( ucfirst( $service ) );
if ( $handler->_enabled )
if ( $method->showInUcp() )
{
$return['social_login'][ $service ] = "complete_profile_{$service}";
$return['social_login'][ $method->id ] = $method->_title;
}
}
@@ -134,7 +129,7 @@ class _Core
$errors = array();
/* Links */
if ( is_numeric( $sigLimits[4] ) and $signature->getElementsByTagName('a')->length > $sigLimits[4] )
if ( is_numeric( $sigLimits[4] ) and ( $signature->getElementsByTagName('a')->length + $signature->getElementsByTagName('iframe')->length ) > $sigLimits[4] )
{
$errors[] = $member->language()->addToStack('sig_num_links_exceeded');
}
@@ -269,6 +264,15 @@ class _Core
$member->pp_gravatar = ( !isset( $values['photo_gravatar_email_public'] ) or $values['photo_gravatar_email_public'] === $member->email ) ? NULL : $values['photo_gravatar_email_public'];
break;
}
if ( $member->pp_photo_type )
{
$member->logHistory( 'core', 'photo', array( 'action' => 'new', 'type' => $member->pp_photo_type ) );
}
else
{
$member->logHistory( 'core', 'photo', array( 'action' => 'remove' ) );
}
}
if ( array_key_exists( 'complete_profile_cover_photo', $values ) )
@@ -281,24 +285,27 @@ class _Core
catch ( \Exception $e ) { }
/* Make sure profile sync services are disabled */
$services = \IPS\core\ProfileSync\ProfileSyncAbstract::services();
if ( !empty( $services ) )
$profileSync = $member->profilesync;
if ( isset( $profileSync['cover'] ) )
{
foreach( $services AS $class )
{
$obj = new $class( $member );
if ( $obj->connected() )
{
$obj->save( array( 'profilesync_cover' => FALSE ) );
}
}
unset( $profileSync['cover'] );
$member->profilesync = $member;
$member->save();
}
$newPhoto = new \IPS\Helpers\CoverPhoto( $values['complete_profile_cover_photo'], 0 );
$member->pp_cover_photo = (string) $newPhoto->file;
$member->pp_cover_offset = (int) $newPhoto->offset;
if ( $newPhoto->file )
{
$member->logHistory( 'core', 'photo', array( 'coverphoto' => 'new' ) );
}
else
{
$member->logHistory( 'core', 'photo', array( 'coverphoto' => 'remove' ) );
}
}
if ( array_key_exists( 'bday', $values ) )
@@ -328,19 +335,41 @@ class _Core
foreach( $step->subcompletion_act as $item )
{
$method = 'completed' . str_replace( ' ', '', ucwords( str_replace( '_', ' ', $item ) ) );
if ( method_exists( $this, $method ) )
if ( $step->completion_act === 'social_login' )
{
return static::$method();
try
{
return \IPS\Login\Handler::load( $item )->canProcess( static::$_member );
}
catch ( \OutOfRangeExcetpion $e )
{
return TRUE;
}
}
else
{
\IPS\Log::debug( "missing_profile_step_method", 'profile_completion' );
return TRUE;
if ( ! static::$_member->group['g_edit_profile'] )
{
/* Member has no permission to edit profile */
return TRUE;
}
$method = 'completed' . str_replace( ' ', '', ucwords( str_replace( '_', ' ', $item ) ) );
if ( method_exists( $this, $method ) )
{
return static::$method();
}
else
{
\IPS\Log::debug( "missing_profile_step_method", 'profile_completion' );
continue;
}
}
}
return TRUE;
}
/**
@@ -357,7 +386,7 @@ class _Core
$return = array_merge( $return, static::wizardBasicProfile() );
$return = array_merge( $return, static::wizardSocial() );
return $return;
}
@@ -425,92 +454,13 @@ class _Core
*/
protected static function completedSignature()
{
return (bool) ( \IPS\Settings::i()->signatures_enabled AND static::$_member->signature );
}
/**
* Connected to Facebook?
*
* @return bool
*/
protected static function completedFacebook()
{
if( !isset( \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Facebook'] ))
if ( ! static::$_member->canEditSignature() )
{
return FALSE;
/* Mark complete as signatures off or no permission to edit profile */
return TRUE;
}
$service = \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Facebook'];
$class = new $service( static::$_member );
return $class->connected();
}
/**
* Connected to Twitter?
*
* @return bool
*/
protected static function completedTwitter()
{
if( !isset( \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Twitter'] ))
{
return FALSE;
}
$service = \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Twitter'];
$class = new $service( static::$_member );
return $class->connected();
}
/**
* Connected to LinkedIn?
*
* @return bool
*/
protected static function completedLinkedin()
{
if( !isset( \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Linkedin'] ))
{
return FALSE;
}
$service = \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Linkedin'];
$class = new $service( static::$_member );
return $class->connected();
}
/**
* Connected to Google?
*
* @return bool
*/
protected static function completedGoogle()
{
if( !isset( \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Google'] ))
{
return FALSE;
}
$service = \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Google'];
$class = new $service( static::$_member );
return $class->connected();
}
/**
* Connected to Live?
*
* @return bool
*/
protected static function completedLive()
{
if( !isset( \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Microsoft'] ))
{
return FALSE;
}
$service = \IPS\core\ProfileSync\ProfileSyncAbstract::services()['Microsoft']; # Inconsistency ftl
$class = new $service( static::$_member );
return $class->connected();
return (bool) ( static::$_member->signature );
}
/* !Wizard Utility Methods */
@@ -535,28 +485,28 @@ class _Core
switch( $item )
{
case 'photo':
if ( !$step->completed( static::$_member ) )
if ( !static::completedPhoto( static::$_member ) )
{
$include['photo'] = $step;
}
break;
case 'birthday':
if ( !$step->completed( static::$_member ) )
if ( !static::completedBirthday( static::$_member ) )
{
$include['birthday'] = $step;
}
break;
case 'signature':
if ( !$step->completed( static::$_member ) )
if ( !static::completedSignature( static::$_member ) )
{
$include['signature'] = $step;
}
break;
case 'cover_photo':
if ( !$step->completed( static::$_member ) )
if ( !static::completedCoverPhoto( static::$_member ) )
{
$include['cover_photo'] = $step;
}
@@ -621,122 +571,56 @@ class _Core
{
foreach( $step->subcompletion_act as $item )
{
switch( $item )
if ( !$step->completed( $member ) )
{
case 'facebook':
if ( !$step->completed( $member ) )
try
{
$method = \IPS\Login\Handler::load( $item );
$return[ $step->key ] = function( $data ) use ( $member, $step, $method )
{
$return[ $step->key ] = function( $data ) use ( $member, $step ) {
$login = \IPS\Login\LoginAbstract::load( 'Facebook' );
$url = \IPS\Http\Url::internal( "app=core&module=system&controller=settings&do=completion", 'front', "settings" );
if ( \IPS\Request::i()->loginProcess )
{
try
$login = new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&do=completion', 'front', 'settings' ), \IPS\Login::LOGIN_UCP );
$login->reauthenticateAs = $member;
$error = NULL;
try
{
if ( $success = $login->authenticate( $method ) )
{
if ( $success->member->member_id === $member->member_id )
{
$login->authenticate( $url, \IPS\Member::loggedIn() );
$method->completeLink( $member, NULL );
return array();
}
catch ( \IPS\Login\Exception $e ) { }
return array();
}
return \IPS\Theme::i()->getTemplate( 'system' )->settingsProfileSyncLogin( $login->loginForm( $url, TRUE ), "profilesync__Facebook" );
};
}
break;
case 'twitter':
if ( !$step->completed( $member ) )
{
$return[ $step->key ] = function( $data ) use ( $member, $step ) {
$login = \IPS\Login\LoginAbstract::load( 'Twitter' );
$url = \IPS\Http\Url::internal( "app=core&module=system&controller=settings&do=completion", 'front', "settings" );
if ( \IPS\Request::i()->loginProcess )
{
try
else
{
$login->authenticate( $url, \IPS\Member::loggedIn() );
$error = \IPS\Member::loggedIn()->language()->addToStack( 'profilesync_already_associated', FALSE, array( 'sprintf' => array( $method->_title ) ) );
}
catch ( \IPS\Login\Exception $e ) { }
return array();
}
return \IPS\Theme::i()->getTemplate( 'system' )->settingsProfileSyncLogin( $login->loginForm( $url, TRUE ), "profilesync__Twitter" );
};
}
break;
case 'google':
if ( !$step->completed( $member ) )
{
$return[ $step->key ] = function( $data ) use ( $member, $step ) {
$login = \IPS\Login\LoginAbstract::load( 'Google' );
$url = \IPS\Http\Url::internal( "app=core&module=system&controller=settings&do=completion", 'front', "settings" );
if ( \IPS\Request::i()->loginProcess )
}
catch ( \IPS\Login\Exception $e )
{
if ( $e->getCode() === \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT )
{
try
if ( $e->member->member_id === $member->member_id )
{
$login->authenticate( $url, \IPS\Member::loggedIn() );
$method->completeLink( $member, NULL );
return array();
}
else
{
$error = \IPS\Member::loggedIn()->language()->addToStack( 'profilesync_email_exists', FALSE, array( 'sprintf' => array( $method->_title ) ) );
}
catch ( \IPS\Login\Exception $e ) { }
return array();
}
return \IPS\Theme::i()->getTemplate( 'system' )->settingsProfileSyncLogin( $login->loginForm( $url, TRUE ), "profilesync__Google" );
};
}
break;
case 'linkedin':
if ( !$step->completed( $member ) )
{
$return[ $step->key ] = function( $data ) use ( $member, $step ) {
$login = \IPS\Login\LoginAbstract::load( 'Linkedin' );
$url = \IPS\Http\Url::internal( "app=core&module=system&controller=settings&do=completion", 'front', "settings" );
if ( \IPS\Request::i()->loginProcess )
else
{
try
{
$login->authenticate( $url, \IPS\Member::loggedIn() );
}
catch ( \IPS\Login\Exception $e ) { }
return array();
$error = $e->getMessage();
}
return \IPS\Theme::i()->getTemplate( 'system' )->settingsProfileSyncLogin( $login->loginForm( $url, TRUE ), "profilesync__Linkedin" );
};
}
break;
case 'live':
if ( !$step->completed( $member ) )
{
$return[ $step->key ] = function( $data ) use ( $member, $step ) {
$login = \IPS\Login\LoginAbstract::load( 'Live' );
$url = \IPS\Http\Url::internal( "app=core&module=system&controller=settings&do=completion", 'front', "settings" );
if ( \IPS\Request::i()->loginProcess )
{
try
{
$login->authenticate( $url, \IPS\Member::loggedIn() );
}
catch ( \IPS\Login\Exception $e ) { }
return array();
}
return \IPS\Theme::i()->getTemplate( 'system' )->settingsProfileSyncLogin( $login->loginForm( $url, TRUE ), "profilesync__Live" );
};
}
break;
}
return \IPS\Theme::i()->getTemplate( 'system' )->profileCompleteSocial( $step, \IPS\Theme::i()->getTemplate( 'system' )->settingsProfileSyncLogin( $method, $login, $error ), \IPS\Request::i()->url() );
};
}
catch ( \OutOfRangeException $e ) { }
}
}
}
@@ -830,7 +714,7 @@ class _Core
/* Validation */
function( $val ) use ( $step )
{
if ( $step->required and ( ! $val['day'] and ! $val['month'] and ! $val['year'] ) )
if ( $step->required and ( ! $val['day'] or ! $val['month'] or ! $val['year'] ) )
{
throw new \InvalidArgumentException('form_required');
}
@@ -852,7 +736,6 @@ class _Core
*/
protected static function signatureForm( &$form, $step, $member )
{
$sigLimits = explode( ":", $member->group['g_signature_limits'] );
$form->add( new \IPS\Helpers\Form\Editor( 'signature', $member->signature, $step->required, array( 'app' => 'core', 'key' => 'Signatures', 'autoSaveKey' => "frontsig-" .$member->member_id, 'attachIds' => array( $member->member_id ) ) ) );
}
@@ -5,7 +5,6 @@
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 16 Nov 2016
*/
@@ -54,7 +53,10 @@ class _ProfileFields
foreach( $fieldData AS $id => $field )
{
$field = \IPS\core\ProfileFields\Field::constructFromData( $field );
$return['profile_fields'][ 'core_pfield_' . $field->_id ] = 'core_pfield_' . $field->_id;
if ( !$field->admin_only )
{
$return['profile_fields'][ 'core_pfield_' . $field->_id ] = 'core_pfield_' . $field->_id;
}
}
}
@@ -94,9 +96,10 @@ class _ProfileFields
{
$member = $member ?: \IPS\Member::loggedIn();
if ( !$member->member_id )
if ( ! $member->group['g_edit_profile'] )
{
return FALSE;
/* Member has no permission to edit profile */
return TRUE;
}
/* Does the member have any profile fields? */
@@ -159,10 +162,7 @@ class _ProfileFields
$fieldId = \substr( $key, 12 );
$update = array();
$update['pf_show_on_reg'] = 1;
if ( $step->required )
{
$update['pf_not_null'] = 1;
}
$update['pf_not_null'] = $step->required;
\IPS\Db::i()->update( 'core_pfields_data', $update, array( "pf_id=?", $fieldId ) );
}
@@ -205,15 +205,9 @@ class _ProfileFields
if ( count( $profileFields ) )
{
try
{
\IPS\Db::i()->select( '*', 'core_pfields_content', array( "member_id=?", $member->member_id ) )->first();
\IPS\Db::i()->update( 'core_pfields_content', $profileFields, array( "member_id=?", $member->member_id ) );
}
catch( \UnderflowException $e )
{
\IPS\Db::i()->insert( 'core_pfields_content', array_merge( array( 'member_id' => $member->member_id ), $profileFields ) );
}
/* Use insert into ... on duplicate key update here to cover both cases where the row exists or does not exist */
\IPS\Db::i()->insert( 'core_pfields_content', array_merge( array( 'member_id' => $member->member_id ), $profileFields ), true );
/* Track and sync the changed custom fields */
$member->changedCustomFields = $profileFields;
$member->save();
@@ -233,16 +227,52 @@ class _ProfileFields
foreach( \IPS\Member\ProfileStep::loadAll() AS $step )
{
try
{
$values = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id = ?', $member->member_id ) )->first();
foreach( $values as $k => $v )
{
if( $k == 'member_id' )
{
continue;
}
$profileFields[ 'core_p' . $k ] = $v;
}
}
catch( \UnderflowException $e )
{
$profileFields = array();
}
if ( $step->completion_act === 'profile_fields' AND ! $step->completed( $member ) )
{
$wizards[ $step->key ] = function( $data ) use ( $member, $step ) {
$wizards[ $step->key ] = function( $data ) use ( $member, $step, $profileFields ) {
$form = new \IPS\Helpers\Form( 'profile_profile_fields_' . $step->id, 'profile_complete_next' );
foreach( $step->subcompletion_act as $item )
{
$id = \substr( $item, 12 );
$field = \IPS\core\ProfileFields\Field::load( $id );
$form->add( $field->buildHelper() );
if ( !$field->admin_only )
{
$value = isset( $profileFields['core_pfield_' . $id] ) ? $profileFields['core_pfield_' . $id] : NULL;
if ( is_array( $value ) and $field->multiple )
{
$value = implode( ',', array_keys( explode( '<br>', $value ) ) );
}
if ( $field->type === 'Editor' )
{
$field::$editorOptions['autoSaveKey'] = md5( get_class( $field ) . '-' . $field->_id . '-' . $member->member_id );
$field::$editorOptions['attachIds'] = array( $member->member_id, $field->_id );
}
$form->add( $field->buildHelper( $value ) );
}
}
if ( $values = $form->values() )
@@ -252,7 +282,7 @@ class _ProfileFields
return $values;
}
return $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'profileCompleteTemplate' ), $step );
};
}
@@ -5,7 +5,6 @@
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 16 Nov 2016
*/
@@ -91,7 +90,7 @@ class _Bulkmail
}
/* Send it */
$email = \IPS\Email::buildFromContent( $mail->subject, $content, NULL, \IPS\Email::TYPE_BULK )
$email = \IPS\Email::buildFromContent( $mail->subject, \IPS\Email::staticParseTextForEmail( $content, \IPS\Lang::load( \IPS\Lang::defaultLanguage() ) ), NULL, \IPS\Email::TYPE_BULK )
->setUnsubscribe( 'core', 'unsubscribeBulk' );
$sent = 0;
foreach ( $recipients as $languageId => $_recipients )
@@ -0,0 +1,90 @@
<?php
/**
* @brief Background Task
* @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 01 Aug 2017
*/
namespace IPS\core\extensions\core\Queue;
/* 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;
}
/**
* Background Task
*/
class _CleanupOrigTables
{
/**
* Parse data before queuing
*
* @param array $data
* @return array
*/
public function preQueueData( $data )
{
return $data;
}
/**
* Run Background Task
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return int New offset
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function run( $data, $offset )
{
/* Get the first table with orig_ and drop it. We do one at a time to prevent timeouts. */
$tables = \IPS\Db::i()->getTables( 'orig_' . \IPS\Db::i()->prefix );
if( !count( $tables ) )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
$table = array_shift( $tables );
/* We call query directly for the drop table statement because we do not want the prefix prepended to the table name */
\IPS\Db::i()->query( "DROP TABLE `{$table}`" );
/* Increment offset and return it */
return ++$offset;
}
/**
* Get Progress
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
* @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function getProgress( $data, $offset )
{
$text = \IPS\Member::loggedIn()->language()->addToStack('orig_cleanup_task', FALSE, array() );
return array( 'text' => $text, 'complete' => $data['originalCount'] ? ( round( 100 / $data['originalCount'] * $offset, 2 ) ) : 100 );
}
/**
* Perform post-completion processing
*
* @param array $data
* @return void
*/
public function postComplete( $data )
{
/* Update the setting so we don't keep checking */
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'orig_tables_checked' ) );
unset( \IPS\Data\Store::i()->settings );
}
}
@@ -0,0 +1,91 @@
<?php
/**
* @brief Background Task
* @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 11 Sep 2017
*/
namespace IPS\core\extensions\core\Queue;
/* 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;
}
/**
* Background Task
*/
class _DeleteImageProxyFiles
{
/**
* Parse data before queuing
*
* @param array $data
* @return array
*/
public function preQueueData( $data )
{
/* Don't run if the image proxy is enabled */
if ( $data['status'] )
{
return NULL;
}
$data['total'] = \IPS\Db::i()->select( 'count(*)', 'core_image_proxy' )->first();
$data['deleted'] = 0;
return $data;
}
/**
* Run Background Task
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return int New offset
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function run( $data, $offset )
{
$select = \IPS\Db::i()->select( 'location', 'core_image_proxy', array(), 'cache_time ASC', 10 );
if ( !$select->count() )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
foreach ( $select as $location )
{
try
{
\IPS\File::get( 'core_Imageproxycache', $location )->delete();
}
catch ( \Exception $e ) { }
\IPS\Db::i()->delete( 'core_image_proxy', array( 'location=?', $location ) );
$data['deleted']++;
}
return 10;
}
/**
* Get Progress
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
* @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function getProgress( $data, $offset )
{
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack('deleting_imageproxy_files'), 'complete' => $data['total'] ? ( round( ( 100 / $data['total'] ) * $data['deleted'], 2 ) ) : 100 );
}
}
@@ -116,7 +116,7 @@ class _DeleteOrMoveContent
if ( isset( $data['moveTo'] ) )
{
$moveTo = $classname::load( $data['moveTo'] );
$text = \IPS\Member::loggedIn()->language()->addToStack('backgroundQueue_move_content', FALSE, array( 'htmlsprintf' => array( "<a href='{$node->url()}' target='_blank'>{$node->_title}</a>", "<a href='{$moveTo->url()}' target='_blank'>{$moveTo->_title}</a>" ) ) );
$text = \IPS\Member::loggedIn()->language()->addToStack('backgroundQueue_move_content', FALSE, array( 'htmlsprintf' => array( "<a href='{$node->url()}' target='_blank'>{$node->_title}</a>", "<a href='{$moveTo->url()}' target='_blank'>" . strip_tags( $moveTo->_title ) . "</a>" ) ) );
}
else
{
@@ -0,0 +1,185 @@
<?php
/**
* @brief Background Task: Restore missing items to deletion log
* @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 19 Oct 2017
*/
namespace IPS\core\extensions\core\Queue;
/* 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;
}
/**
* Background Task: Restore missing items to deletion log
*/
class _FixDeletionLog
{
/**
* @brief Number of entries per cycle to restore
*/
public $perCycle = 500;
/**
* Parse data before queuing
*
* @param array $data
* @return array
*/
public function preQueueData( $data )
{
$classname = $data['class'];
/* If this class doesn't support hiding we can't use deletion log */
if ( !in_array( 'IPS\Content\Hideable', class_implements( $classname ) ) )
{
throw new \OutOfRangeException;
}
\IPS\Log::debug( "Getting preQueueData for " . $classname, 'fixDeletionLog' );
try
{
$where = array();
$where[] = array( 'dellog_id IS NULL' );
if ( isset( $classname::$databaseColumnMap['approved'] ) )
{
$where[] = array( $classname::$databaseTable . '.' . $classname::$databasePrefix . $classname::$databaseColumnMap['approved'] . '=?', -2 );
}
elseif ( isset( $classname::$databaseColumnMap['hidden'] ) )
{
$where[] = array( $classname::$databaseTable . '.' . $classname::$databasePrefix . $classname::$databaseColumnMap['hidden'] . '=?', -2 );
}
else
{
/* Failsafe */
throw new \OutOfRangeException;
}
$select = \IPS\Db::i()->select( 'count(*)', $classname::$databaseTable, $where )->join( 'core_deletion_log', array( 'dellog_content_class=? and dellog_content_id=' . $classname::$databaseTable . '.' . $classname::$databasePrefix . $classname::$databaseColumnId, $classname ) );
$data['count'] = $select->first();
}
catch( \Exception $ex )
{
throw new \OutOfRangeException;
}
if( $data['count'] == 0 )
{
return null;
}
return $data;
}
/**
* Run Background Task
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return int New offset
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function run( $data, $offset )
{
$classname = $data['class'];
$exploded = explode( '\\', $classname );
if ( !class_exists( $classname ) or !\IPS\Application::appIsEnabled( $exploded[1] ) )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
$indexed = 0;
\IPS\Log::debug( "Running " . $classname . ", with an offset of " . $offset, 'fixDeletionLog' );
/* This could be a pages database that has since been deleted */
try
{
$where = array();
$where[] = array( 'dellog_id IS NULL' );
if ( isset( $classname::$databaseColumnMap['approved'] ) )
{
$where[] = array( $classname::$databaseTable . '.' . $classname::$databasePrefix . $classname::$databaseColumnMap['approved'] . '=?', -2 );
}
elseif ( isset( $classname::$databaseColumnMap['hidden'] ) )
{
$where[] = array( $classname::$databaseTable . '.' . $classname::$databasePrefix . $classname::$databaseColumnMap['hidden'] . '=?', -2 );
}
$select = \IPS\Db::i()->select( $classname::$databaseTable . '.*', $classname::$databaseTable, $where )->join( 'core_deletion_log', array( 'dellog_content_class=? and dellog_content_id=' . $classname::$databaseTable . '.' . $classname::$databasePrefix . $classname::$databaseColumnId, $classname ) );
$iterator = new \IPS\Patterns\ActiveRecordIterator( $select, $classname );
foreach( $iterator as $item )
{
$idColumn = $classname::$databaseColumnId;
/* Try to figure out member */
$memberId = NULL;
try
{
if ( in_array( 'IPS\Content\Review', class_parents( $classname ) ) )
{
$memberId = \IPS\Db::i()->select( 'member_id', 'core_moderator_logs', array( 'class=? AND item_id=? AND ( lang_key=? AND note LIKE ? )', $classname, $item->$idColumn, 'modlog__action_delete', '%review%' ) )->first();
}
elseif ( in_array( 'IPS\Content\Comment', class_parents( $classname ) ) )
{
$memberId = \IPS\Db::i()->select( 'member_id', 'core_moderator_logs', array( 'class=? AND item_id=? AND lang_key=?', $classname, $item->$idColumn, 'modlog__comment_delete' ) )->first();
}
else
{
$memberId = \IPS\Db::i()->select( 'member_id', 'core_moderator_logs', array( 'class=? AND item_id=? AND lang_key=?', $classname, $item->$idColumn, 'modlog__action_delete' ) )->first();
}
}
catch( \Exception $e ){}
$log = new \IPS\core\DeletionLog;
$log->setContentAndMember( $item, $memberId ? \IPS\Member::load( $memberId ) : NULL );
$log->save();
$indexed++;
}
}
catch( \Exception $e )
{
\IPS\Log::log( $e, 'deletion_log' );
throw new \IPS\Task\Queue\OutOfRangeException;
}
/* If we didn't find any we are done */
if( !$indexed )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
return ( $offset + $indexed );
}
/**
* Get Progress
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
*/
public function getProgress( $data, $offset )
{
$class = $data['class'];
$exploded = explode( '\\', $class );
if ( !class_exists( $class ) or !\IPS\Application::appIsEnabled( $exploded[1] ) )
{
throw new \OutOfRangeException;
}
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack('fixdellog_task', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $class::$title, FALSE, array( 'strtolower' => TRUE ) ) ) ) ), 'complete' => $data['count'] ? ( round( 100 / $data['count'] * $offset, 2 ) ) : 100 );
}
}
@@ -89,7 +89,7 @@ class _Follow
$complete = 100;
}
$title = ( $item instanceof \IPS\Content\Comment ) ? $item->item()->mapped('title') : $item->mapped('title');
$title = strip_tags( ( $item instanceof \IPS\Content\Comment ) ? $item->item()->mapped('title') : $item->mapped('title') );
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack('backgroundQueue_follow', FALSE, array( 'htmlsprintf' => array( "<a href='{$item->url()}' target='_blank'>{$title}</a>" ) ) ), 'complete' => $complete );
}
}
@@ -0,0 +1,117 @@
<?php
/**
* @brief Background Task
* @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 14 Nov 2017
*/
namespace IPS\core\extensions\core\Queue;
/* 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;
}
/**
* Background Task
*/
class _InstallLanguage
{
/**
* Parse data before queuing
*
* @param array $data
* @return array
*/
public function preQueueData( $data )
{
return $data;
}
/**
* Run Background Task
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return int New offset
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function run( $data, $offset )
{
if( \IPS\Application::appIsEnabled( $data['application'] ) AND file_exists( \IPS\ROOT_PATH . "/applications/{$data['application']}/data/lang.xml" ) )
{
/* Open XML file */
$xml = new \IPS\Xml\XMLReader;
$xml->open( \IPS\ROOT_PATH . "/applications/{$data['application']}/data/lang.xml" );
$xml->read();
/* Get the version */
$xml->read();
$xml->read();
$version = $xml->getAttribute('version');
$inserts = array();
/* Start looping through each word */
while ( $xml->read() )
{
if( $xml->name != 'word' OR $xml->nodeType != \XMLReader::ELEMENT )
{
continue;
}
$key = $xml->getAttribute('key');
$value = $xml->readString();
$inserts[] = array(
'word_app' => $data['application'],
'word_key' => $key,
'lang_id' => $data['language_id'],
'word_default' => $value,
'word_default_version' => $version,
'word_js' => (int) $xml->getAttribute('js'),
'word_export' => 1,
);
$xml->next();
}
\IPS\Db::i()->insert( 'core_sys_lang_words', $inserts, TRUE );
throw new \IPS\Task\Queue\OutOfRangeException;
}
else
{
/* Application doesn't exist anymore */
throw new \IPS\Task\Queue\OutOfRangeException;
}
}
/**
* Get Progress
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
* @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function getProgress( $data, $offset )
{
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack( 'backgroundQueue_importing_english', FALSE, array( 'sprintf' => \IPS\Application::load( $data['application'] )->_title ) ), 'complete' => NULL );
}
/**
* Perform post-completion processing
*
* @param array $data
* @return void
*/
public function postComplete( $data )
{
$data = json_decode($data['data'], TRUE);
\IPS\Db::i()->insert( 'core_sys_lang_words', \IPS\Db::i()->select( "NULL AS word_id, {$data['language_id']} AS lang_id, word_app, word_plugin, word_theme, word_key, word_custom, word_default, word_default_version, word_custom_version, word_js, word_export", 'core_sys_lang_words', array( "word_export=? AND word_app=? AND lang_id=?", 0, $data['application'], \IPS\Lang::defaultLanguage() ) ) );
unset( \IPS\Data\Store::i()->languages );
}
}
@@ -73,10 +73,15 @@ class _MoveFiles
{
$settings[ $data['storageExtension'] ] = $data['newConfiguration'];
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => json_encode( $settings ) ), array( 'conf_key=?', 'upload_settings' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'upload_settings' => json_encode( $settings ) ) );
}
/* Run the settingsUpdated method to clear out anything that needs rebuilding or whatever and so on */
if ( method_exists( $extension, 'settingsUpdated' ) )
{
$extension::settingsUpdated( $data['newConfiguration'] );
}
throw new \IPS\Task\Queue\OutOfRangeException;
}
catch ( \Exception $e )
@@ -75,6 +75,7 @@ class _MoveMembers
}
$newGroup = \IPS\Member\Group::load( $data['group'] );
$by = isset( $data['by'] ) ? \IPS\Member::load( $data['by'] ) : FALSE;
foreach( $select AS $row )
{
@@ -103,7 +104,11 @@ class _MoveMembers
$member->mgroup_others = implode( ',', array_diff( $extraGroups, array( $data['oldGroup'] ) ) );
}
if ( $member->member_group_id != $newGroup->g_id )
{
$member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'mass', 'old' => $member->member_group_id, 'new' => $newGroup->g_id ), $by );
}
$member->member_group_id = $newGroup->g_id;
$member->save();
}
@@ -131,6 +131,10 @@ class _RebuildContentImages
{
$class = $data['class'];
$exploded = explode( '\\', $class );
if ( !class_exists( $class ) OR !\IPS\Application::appIsEnabled( $exploded[1] ) )
{
throw new \OutOfRangeException;
}
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack('rebuilding_content_images', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $class::$title, FALSE, array( 'strtolower' => TRUE ) ) ) ) ), 'complete' => $data['realCount'] ? ( round( 100 / $data['realCount'] * $data['indexed'], 2 ) ) : 100 );
}
Loaded 100 of 1349 files, more files were not shown because too many files have changed in this diff. Show more