Version 4.7.13
This commit is contained in:
1 parent
fd22d99e69
commit
7124a02564
954 files changed
+29277
-59308
No files matched your search
+256
-172
@@ -539,202 +539,240 @@ class _Club extends \IPS\Patterns\ActiveRecord implements \IPS\Content\Embeddabl
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'club_rules_reacknowledge', FALSE, FALSE, [], NULL, NULL, NULL, 'club_rules_reacknowledge' ) );
|
||||
}
|
||||
|
||||
if ( $values = $form->values() )
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Club Form
|
||||
*
|
||||
* @param $values
|
||||
* @param bool $acp TRUE if editing in the ACP
|
||||
* @param bool $new TRUE if creating new
|
||||
* @param array|null $availableTypes If creating new, the available types
|
||||
* @return \IPS\Helpers\Form|NULL
|
||||
*/
|
||||
public function processForm($values, bool $acp, bool $new = FALSE, array $availableTypes = NULL): ?\IPS\Helpers\Form
|
||||
{
|
||||
$this->name = $values['club_name'];
|
||||
|
||||
/* If there is only one type available, set it. */
|
||||
if( \is_array( $availableTypes ) AND \count( $availableTypes ) == 1 )
|
||||
{
|
||||
$this->name = $values['club_name'];
|
||||
$values['club_type'] = key( $availableTypes );
|
||||
}
|
||||
|
||||
/* If there is only one type available, set it. */
|
||||
if( \is_array( $availableTypes ) AND \count( $availableTypes ) == 1 )
|
||||
{
|
||||
$values['club_type'] = key( $availableTypes );
|
||||
}
|
||||
|
||||
$needToUpdatePermissions = FALSE;
|
||||
if ( $acp )
|
||||
{
|
||||
if ( $this->type != $values['club_type'] )
|
||||
{
|
||||
$this->type = $values['club_type'];
|
||||
$needToUpdatePermissions = TRUE;
|
||||
}
|
||||
if ( $this->owner != $values['club_owner'] )
|
||||
{
|
||||
$this->owner = $values['club_owner'];
|
||||
$this->addMember( $values['club_owner'], \IPS\Member\Club::STATUS_LEADER, TRUE );
|
||||
}
|
||||
}
|
||||
elseif ( $new )
|
||||
$needToUpdatePermissions = FALSE;
|
||||
if ( $acp )
|
||||
{
|
||||
if ( $this->type != $values['club_type'] )
|
||||
{
|
||||
$this->type = $values['club_type'];
|
||||
$this->owner = \IPS\Member::loggedIn();
|
||||
$needToUpdatePermissions = TRUE;
|
||||
}
|
||||
|
||||
$this->about = $values['club_about'];
|
||||
$this->profile_photo = (string) $values['club_profile_photo'];
|
||||
|
||||
if( $values['club_profile_photo'] )
|
||||
if ( $this->owner != $values['club_owner'] )
|
||||
{
|
||||
$this->profile_photo_uncropped = (string) $values['club_profile_photo'];
|
||||
$this->owner = $values['club_owner'];
|
||||
$this->addMember( $values['club_owner'], Club::STATUS_LEADER, TRUE );
|
||||
|
||||
/* Update purchases for commission */
|
||||
if( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on )
|
||||
{
|
||||
\IPS\Db::i()->update( 'nexus_purchases', array( 'ps_pay_to' => $this->owner ), array( "ps_pay_to IS NOT NULL and ps_app=? and ps_type=? and ps_item_id=?", 'core', 'club', $this->id ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $values['club_location'] ) )
|
||||
}
|
||||
elseif ( $new )
|
||||
{
|
||||
$this->type = $values['club_type'];
|
||||
$this->owner = \IPS\Member::loggedIn();
|
||||
}
|
||||
|
||||
$this->about = $values['club_about'];
|
||||
$this->profile_photo = (string) $values['club_profile_photo'];
|
||||
|
||||
if( $values['club_profile_photo'] )
|
||||
{
|
||||
$this->profile_photo_uncropped = (string) $values['club_profile_photo'];
|
||||
}
|
||||
|
||||
if ( isset( $values['club_location'] ) )
|
||||
{
|
||||
$this->location_json = json_encode( $values['club_location'] );
|
||||
if ( $values['club_location']->lat and $values['club_location']->long )
|
||||
{
|
||||
$this->location_json = json_encode( $values['club_location'] );
|
||||
if ( $values['club_location']->lat and $values['club_location']->long )
|
||||
{
|
||||
$this->location_lat = $values['club_location']->lat;
|
||||
$this->location_long = $values['club_location']->long;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->location_lat = NULL;
|
||||
$this->location_long = NULL;
|
||||
}
|
||||
$this->location_lat = $values['club_location']->lat;
|
||||
$this->location_long = $values['club_location']->long;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->location_json = NULL;
|
||||
$this->location_lat = NULL;
|
||||
$this->location_long = NULL;
|
||||
}
|
||||
|
||||
if ( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on and \IPS\Member::loggedIn()->group['gbw_paid_clubs'] )
|
||||
{
|
||||
switch ( $values['club_membership_fee'] )
|
||||
{
|
||||
case 'free':
|
||||
$this->fee = NULL;
|
||||
$this->renewal_term = 0;
|
||||
$this->renewal_units = NULL;
|
||||
$this->renewal_price = NULL;
|
||||
break;
|
||||
|
||||
case 'paid':
|
||||
$this->fee = json_encode( $values['club_fee'] );
|
||||
if ( $values['club_renewals'] and $values['club_renewal_term'] )
|
||||
{
|
||||
$term = $values['club_renewal_term']->getTerm();
|
||||
$this->renewal_term = $term['term'];
|
||||
$this->renewal_units = $term['unit'];
|
||||
$this->renewal_price = json_encode( $values['club_renewal_term']->cost );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->renewal_term = 0;
|
||||
$this->renewal_units = NULL;
|
||||
$this->renewal_price = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->location_json = NULL;
|
||||
$this->location_lat = NULL;
|
||||
$this->location_long = NULL;
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'club_show_membertab', $values ) )
|
||||
if ( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on and \IPS\Member::loggedIn()->group['gbw_paid_clubs'] )
|
||||
{
|
||||
switch ( $values['club_membership_fee'] )
|
||||
{
|
||||
$this->show_membertab = $values['club_show_membertab'];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Default is "Everybody" */
|
||||
$this->show_membertab = "nonmember";
|
||||
}
|
||||
case 'free':
|
||||
$this->fee = NULL;
|
||||
$this->renewal_term = 0;
|
||||
$this->renewal_units = NULL;
|
||||
$this->renewal_price = NULL;
|
||||
break;
|
||||
|
||||
$this->save();
|
||||
|
||||
if ( $values['show_rules'] )
|
||||
{
|
||||
$this->rules = $values['club_rules'];
|
||||
\IPS\File::claimAttachments( ( $new ) ? "club-rules-new" : "club-rules-{$this->id}", $this->id, NULL, 'rules' );
|
||||
$this->rules_required = (bool) $values['club_rules_required'];
|
||||
|
||||
/* Do we need to reset the acknowledge flags? */
|
||||
if ( !$new AND \array_key_exists( 'club_rules_reacknowledge', $values ) )
|
||||
{
|
||||
if ( $values['club_rules_reacknowledge'] )
|
||||
case 'paid':
|
||||
$this->fee = json_encode( $values['club_fee'] );
|
||||
|
||||
if ( $values['club_renewals'] and $values['club_renewal_term'] )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_clubs_memberships', array( "rules_acknowledged" => FALSE ), array( "club_id=?", $this->id ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->rules = NULL;
|
||||
$this->rules_required = FALSE;
|
||||
|
||||
if ( !$new )
|
||||
{
|
||||
/* If this isn't a new club, update all memberships in case they decide to re-add rules later on */
|
||||
\IPS\Db::i()->update( "core_clubs_memberships", array( 'rules_acknowledged' => FALSE ), array( "club_id=?", $this->id ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $new )
|
||||
{
|
||||
$this->addMember( \IPS\Member::loggedIn(), \IPS\Member\Club::STATUS_LEADER );
|
||||
$this->acknowledgeRules( \IPS\Member::loggedIn() );
|
||||
}
|
||||
$this->recountMembers();
|
||||
|
||||
$customFieldValues = array();
|
||||
foreach ( \IPS\Member\Club\CustomField::roots() as $field )
|
||||
{
|
||||
if ( isset( $values["core_clubfield_{$field->id}"] ) )
|
||||
{
|
||||
$helper = $field->buildHelper();
|
||||
|
||||
if ( $helper instanceof \IPS\Helpers\Form\Upload )
|
||||
{
|
||||
$customFieldValues[ "field_{$field->id}" ] = (string) $values["core_clubfield_{$field->id}"];
|
||||
$term = $values['club_renewal_term']->getTerm();
|
||||
$this->renewal_term = $term['term'];
|
||||
$this->renewal_units = $term['unit'];
|
||||
$this->renewal_price = json_encode( $values['club_renewal_term']->cost );
|
||||
}
|
||||
else
|
||||
{
|
||||
$customFieldValues[ "field_{$field->id}" ] = $helper::stringValue( $values["core_clubfield_{$field->id}"] );
|
||||
$this->renewal_term = 0;
|
||||
$this->renewal_units = NULL;
|
||||
$this->renewal_price = NULL;
|
||||
}
|
||||
|
||||
if ( $field->type === 'Editor' )
|
||||
{
|
||||
$field->claimAttachments( $this->id );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ( \count( $customFieldValues ) )
|
||||
{
|
||||
$customFieldValues['club_id'] = $this->id;
|
||||
\IPS\Db::i()->insert( 'core_clubs_fieldvalues', $customFieldValues, TRUE );
|
||||
}
|
||||
|
||||
if ( $needToUpdatePermissions )
|
||||
{
|
||||
foreach ( $this->nodes() as $node )
|
||||
{
|
||||
try
|
||||
{
|
||||
$nodeClass = $node['node_class'];
|
||||
$node = $nodeClass::load( $node['node_id'] );
|
||||
$node->setPermissionsToClub( $this );
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
if( $new and \IPS\Settings::i()->clubs_require_approval and !$this->approved )
|
||||
{
|
||||
$this->sendModeratorApprovalNotification();
|
||||
}
|
||||
|
||||
if( $new and $this->approved )
|
||||
{
|
||||
\IPS\Api\Webhook::fire( 'club_created', $this );
|
||||
}
|
||||
else if( !$new AND isset( $this->changed['approved'] ) )
|
||||
{
|
||||
$this->onApprove();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $form;
|
||||
|
||||
if ( array_key_exists( 'club_show_membertab', $values ) )
|
||||
{
|
||||
$this->show_membertab = $values['club_show_membertab'];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Default is "Everybody" */
|
||||
$this->show_membertab = "nonmember";
|
||||
}
|
||||
|
||||
$this->save();
|
||||
|
||||
if ( $values['show_rules'] )
|
||||
{
|
||||
$this->rules = $values['club_rules'];
|
||||
\IPS\File::claimAttachments( ( $new ) ? "club-rules-new" : "club-rules-{$this->id}", $this->id, NULL, 'rules' );
|
||||
$this->rules_required = (bool) $values['club_rules_required'];
|
||||
|
||||
/* Do we need to reset the acknowledge flags? */
|
||||
if ( !$new AND \array_key_exists( 'club_rules_reacknowledge', $values ) )
|
||||
{
|
||||
if ( $values['club_rules_reacknowledge'] )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_clubs_memberships', array( "rules_acknowledged" => FALSE ), array( "club_id=?", $this->id ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->rules = NULL;
|
||||
$this->rules_required = FALSE;
|
||||
|
||||
if ( !$new )
|
||||
{
|
||||
/* If this isn't a new club, update all memberships in case they decide to re-add rules later on */
|
||||
\IPS\Db::i()->update( "core_clubs_memberships", array( 'rules_acknowledged' => FALSE ), array( "club_id=?", $this->id ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $new )
|
||||
{
|
||||
$this->addMember( \IPS\Member::loggedIn(), Club::STATUS_LEADER );
|
||||
$this->acknowledgeRules( \IPS\Member::loggedIn() );
|
||||
}
|
||||
$this->recountMembers();
|
||||
|
||||
$customFieldValues = array();
|
||||
foreach ( \IPS\Member\Club\CustomField::roots() as $field )
|
||||
{
|
||||
if ( isset( $values["core_clubfield_{$field->id}"] ) )
|
||||
{
|
||||
$helper = $field->buildHelper();
|
||||
|
||||
if ( $helper instanceof \IPS\Helpers\Form\Upload )
|
||||
{
|
||||
$customFieldValues[ "field_{$field->id}" ] = (string) $values["core_clubfield_{$field->id}"];
|
||||
}
|
||||
else
|
||||
{
|
||||
$customFieldValues[ "field_{$field->id}" ] = $helper::stringValue( $values["core_clubfield_{$field->id}"] );
|
||||
}
|
||||
|
||||
if ( $field->type === 'Editor' )
|
||||
{
|
||||
$field->claimAttachments( $this->id );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( \count( $customFieldValues ) )
|
||||
{
|
||||
$customFieldValues['club_id'] = $this->id;
|
||||
\IPS\Db::i()->insert( 'core_clubs_fieldvalues', $customFieldValues, TRUE );
|
||||
}
|
||||
|
||||
if ( $needToUpdatePermissions )
|
||||
{
|
||||
foreach ( $this->nodes() as $node )
|
||||
{
|
||||
try
|
||||
{
|
||||
$nodeClass = $node['node_class'];
|
||||
$node = $nodeClass::load( $node['node_id'] );
|
||||
$node->setPermissionsToClub( $this );
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
if( $new and \IPS\Settings::i()->clubs_require_approval and !$this->approved )
|
||||
{
|
||||
$this->sendModeratorApprovalNotification();
|
||||
}
|
||||
|
||||
if( $new and $this->approved )
|
||||
{
|
||||
\IPS\Api\Webhook::fire( 'club_created', $this );
|
||||
}
|
||||
else if( !$new AND isset( $this->changed['approved'] ) )
|
||||
{
|
||||
$this->onApprove();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect after save
|
||||
*
|
||||
* @param \IPS\Member\Club $old A clone of the club as it was before
|
||||
* @param \IPS\Member\Club\ $new The club now
|
||||
* @return array
|
||||
*/
|
||||
public static function renewalChanges(Club $old, Club $new)
|
||||
{
|
||||
$changes = array();
|
||||
|
||||
foreach ( array( 'renewal_term', 'renewal_units', 'renewal_price' ) as $k )
|
||||
{
|
||||
if ( $old->$k != $new->$k )
|
||||
{
|
||||
$changes[ $k ] = $old->$k;
|
||||
}
|
||||
}
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2191,4 +2229,50 @@ class _Club extends \IPS\Patterns\ActiveRecord implements \IPS\Content\Embeddabl
|
||||
\IPS\Api\Webhook::fire( 'club_created', $this );
|
||||
$this->owner->achievementAction( 'core', 'NewClub', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update existing purchases
|
||||
*
|
||||
* @param \IPS\nexus\Purchase $purchase The purchase
|
||||
* @param array $changes The old values
|
||||
* @param bool $cancelBillingAgreementIfNecessary If making changes to renewal terms, TRUE will cancel associated billing agreements. FALSE will skip that change
|
||||
* @return void
|
||||
*/
|
||||
public function updatePurchase( \IPS\nexus\Purchase $purchase, $changes=array(), $cancelBillingAgreementIfNecessary=FALSE )
|
||||
{
|
||||
$tax = NULL;
|
||||
if ( $purchase->tax )
|
||||
{
|
||||
try
|
||||
{
|
||||
$tax = \IPS\nexus\Tax::load( $purchase->tax );
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
}
|
||||
|
||||
$currency = $purchase->renewal_currency ?: $purchase->member->defaultCurrency( );
|
||||
|
||||
$price = json_decode( $this->renewal_price, TRUE );
|
||||
|
||||
$purchase->renewals = new \IPS\nexus\Purchase\RenewalTerm(
|
||||
new \IPS\nexus\Money( $price[$currency]['amount'], $currency ),
|
||||
new \DateInterval( 'P' . $this->renewal_term . mb_strtoupper( $this->renewal_units ) ),
|
||||
$tax
|
||||
);
|
||||
|
||||
if ( $cancelBillingAgreementIfNecessary and $billingAgreement = $purchase->billing_agreement )
|
||||
{
|
||||
if ( array_key_exists( 'renewal_price', $changes ) and !empty( $changes['renewal_price'] ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$billingAgreement->cancel();
|
||||
$billingAgreement->save();
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
$purchase->save();
|
||||
}
|
||||
}
|
||||
@@ -262,7 +262,7 @@ class _Device extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
try
|
||||
{
|
||||
$location = \IPS\GeoLocation::getByIp( \IPS\Request::i()->ipAddress() );
|
||||
$location = \IPS\GeoLocation::getRequesterLocation();
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
|
||||
+334
-12
@@ -120,6 +120,8 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
'new_device_email' => 16777216, // Send an email when a new device is used to log in.
|
||||
'no_solved_reenage' => 33554432, // User does not want to get topic re-engagement emails
|
||||
'datalayer_pii_optout' => 67108864, // User has opted in to having their PII collected by datalayer
|
||||
'email_messages_bounce' => 134217728, // User's email keeps returning hard bounces so they need to change it
|
||||
// 268435456 - cookie_optout moved to cookie preferences
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -133,7 +135,6 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
'last_visit',
|
||||
'last_activity',
|
||||
'profilesync_lastsync',
|
||||
|
||||
];
|
||||
|
||||
/* !Follow */
|
||||
@@ -203,7 +204,12 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
if ( !static::$loggedInMember->member_id and isset( \IPS\Request::i()->cookie['ipsTimezone'] ) )
|
||||
{
|
||||
static::$loggedInMember->timezone = \IPS\DateTime::getFixedTimezone( \IPS\Request::i()->cookie['ipsTimezone'] );
|
||||
/* Check for valid timezone identifier */
|
||||
$tz = \IPS\DateTime::getFixedTimezone( \IPS\Request::i()->cookie['ipsTimezone'] );
|
||||
if( \in_array( $tz, \DateTimeZone::listIdentifiers() ) )
|
||||
{
|
||||
static::$loggedInMember->timezone = $tz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +337,47 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow optional cookies
|
||||
*
|
||||
* @param bool $allow
|
||||
* @return bool
|
||||
*/
|
||||
public function setAllowOptionalCookies( bool $allow = TRUE ): bool
|
||||
{
|
||||
$expire = ( new \IPS\DateTime )->add( new \DateInterval('P6M' ) );
|
||||
\IPS\Request::i()->setCookie( 'cookie_consent', 1, $expire, FALSE );
|
||||
|
||||
if( !$allow )
|
||||
{
|
||||
/* Remove optional cookies */
|
||||
foreach( \IPS\Request::i()->cookie as $cookieName => $value )
|
||||
{
|
||||
if( !\in_array( $cookieName, \IPS\Request::i()->getEssentialCookies() ) )
|
||||
{
|
||||
\IPS\Request::i()->setCookie( $cookieName, NULL);
|
||||
}
|
||||
}
|
||||
\IPS\Request::i()->setCookie( 'cookie_consent_optional', NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Request::i()->setCookie( 'cookie_consent_optional', 1, $expire, FALSE );
|
||||
}
|
||||
|
||||
return $allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are optional cookies allowed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_optionalCookiesAllowed(): bool
|
||||
{
|
||||
return isset( \IPS\Request::i()->cookie[ 'cookie_consent' ] ) AND isset( \IPS\Request::i()->cookie[ 'cookie_consent_optional' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Delete Record
|
||||
*
|
||||
@@ -411,6 +458,7 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
/* @note The completed column is added in the 4.4.0 Beta 1 routine, so we need to verify the upgrade has been performed otherwise
|
||||
the auto-upgrader can replace this file before the column is added, and an SQL error can prevent the admin from completing the
|
||||
auto-upgrade process */
|
||||
$fireRegistrationCompletedWebhook = FALSE;
|
||||
if ( \IPS\Application::load( 'core' )->long_version > 104000 )
|
||||
{
|
||||
if( $this->completed AND ( !$this->name OR !$this->email ) )
|
||||
@@ -421,22 +469,32 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
elseif( !$this->completed AND $this->name AND $this->email )
|
||||
{
|
||||
$this->completed = TRUE;
|
||||
\IPS\Api\Webhook::fire( 'member_registration_complete', $this, $this->webhookFilters() );
|
||||
$fireRegistrationCompletedWebhook = TRUE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If the email was changed, make sure that the bounce flag is unset so the member no longer sees the error message */
|
||||
if ( isset( $changes['email'] ) )
|
||||
{
|
||||
$this->members_bitoptions['email_messages_bounce'] = 0;
|
||||
}
|
||||
|
||||
parent::save();
|
||||
|
||||
if ( $new )
|
||||
{
|
||||
$this->memberSync( 'onCreateAccount' );
|
||||
|
||||
/* Profile Fields */
|
||||
\IPS\Db::i()->insert( 'core_pfields_content', array( 'member_id' => $this->member_id ), TRUE );
|
||||
|
||||
$this->memberSync( 'onCreateAccount' );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $fireRegistrationCompletedWebhook )
|
||||
{
|
||||
\IPS\Api\Webhook::fire( 'member_registration_complete', $this, $this->webhookFilters() );
|
||||
}
|
||||
/* Run member sync, but not if the only change is the last_activity timestamp */
|
||||
if( \count( $this->changedCustomFields ) > 0 OR ( \count( $changes ) > 0 AND !( \count( $changes ) === 1 AND isset( $changes['last_activity'] ) ) ) )
|
||||
{
|
||||
@@ -784,6 +842,12 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
$this->members_bitoptions['validating'] = FALSE;
|
||||
}
|
||||
|
||||
/* Revoke oAuth tokens */
|
||||
if( $value )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'member_id=?', $this->member_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2011,7 +2075,14 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return $this->_lang;
|
||||
}
|
||||
|
||||
|
||||
/* If in API use the lang set by dispatcher */
|
||||
if ( \IPS\Dispatcher::hasInstance() and class_exists( 'IPS\Dispatcher', FALSE ) AND \IPS\Dispatcher::i()->controllerLocation === 'api' )
|
||||
{
|
||||
$this->_lang = \IPS\Dispatcher::i()->_setLanguage();
|
||||
return $this->_lang;
|
||||
}
|
||||
|
||||
/* If in setup, create a "dummy" language */
|
||||
if ( \IPS\Dispatcher::hasInstance() and class_exists( 'IPS\Dispatcher', FALSE ) AND \IPS\Dispatcher::i()->controllerLocation === 'setup' AND \IPS\Dispatcher::i()->setupLocation === 'install' )
|
||||
{
|
||||
@@ -3418,7 +3489,6 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
* @apiresponse string title Member title
|
||||
* @clientapiresponse string timezone Member timezone
|
||||
* @apiresponse string formattedName Username with group formatting
|
||||
* @apiresponse string ipAddress IP address used during registration
|
||||
* @apiresponse \IPS\Member\Group primaryGroup Primary group
|
||||
* @clientapiresponse [\IPS\Member\Group] secondaryGroups Secondary groups
|
||||
* @clientapiresponse string email Email address
|
||||
@@ -3445,7 +3515,17 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL, $otherFields = NULL )
|
||||
{
|
||||
$group = \IPS\Member\Group::load( $this->_data['member_group_id'] );
|
||||
try
|
||||
{
|
||||
$group = \IPS\Member\Group::load( $this->_data['member_group_id'] );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Log::log( "{$this->name} has an invalid group ({$this->_data['member_group_id']}) during API call. Resetting.", 'api_invalid_group' );
|
||||
$group = \IPS\Member\Group::load( \IPS\Settings::i()->member_group ); // Intentially no catch here as that means things are really broken.
|
||||
$this->member_group_id = \IPS\Settings::i()->member_group;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$secondaryGroups = array();
|
||||
foreach ( array_filter( array_map( "intval", explode( ',', $this->_data['mgroup_others'] ) ) ) as $secondaryGroupId )
|
||||
@@ -3471,7 +3551,8 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
foreach( $profileFields as $field )
|
||||
{
|
||||
if ( !$authorizedMember or
|
||||
if ( !$authorizedMember or
|
||||
$field['pf_contains_pii'] == 1 AND ( $authorizedMember->member_id == $this->member_id ) or
|
||||
$field['pf_member_hide'] == 'all' or
|
||||
( $field['pf_member_hide'] == 'owner' and ( $authorizedMember->member_id == $this->member_id OR $authorizedMember->modPermissions() OR $authorizedMember->isAdmin() ) ) or
|
||||
( $field['pf_member_hide'] == 'staff' and ( $authorizedMember->modPermissions() OR $authorizedMember->isAdmin() ) )
|
||||
@@ -3914,9 +3995,11 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
* @param string $type Request type
|
||||
* @param string $emailAddress Email address to check, NULL for existing email address
|
||||
* @param int $spamCode If set, will modify by reference with the raw value from the spam service
|
||||
* @param bool $disposable If set, will modify by reference if the user used a disposable email.
|
||||
* @param bool $geoBlock If set, will modify by reference if the user is in a moderated / blocked country.
|
||||
* @return int|NULL Action code based on spam service response, or NULL for no action
|
||||
*/
|
||||
public function spamService( $type='register', $emailAddress=NULL, &$spamCode=NULL )
|
||||
public function spamService( $type='register', $emailAddress=NULL, &$spamCode=NULL, &$disposable=FALSE, &$geoBlock=FALSE )
|
||||
{
|
||||
$email = $emailAddress ?: $this->email;
|
||||
|
||||
@@ -4004,10 +4087,156 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* GeoLocation */
|
||||
if ( $type === 'register' AND $action == 1 )
|
||||
{
|
||||
$action = $this->geoSpamCheck( $geoBlock );
|
||||
}
|
||||
|
||||
/* If the normal spam service and geolocation didn't pick up anything, check for a disposable email. */
|
||||
if ( $type === 'register' AND $action == 1 )
|
||||
{
|
||||
$action = $this->disposableEmailCheck( $emailAddress, $disposable );
|
||||
}
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* GeoLocation Block Check
|
||||
*
|
||||
* @param bool $geoBlock If set, will modify by reference if the user is in a moderated / blocked country.
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function geoSpamCheck( &$geoBlock ): ?int
|
||||
{
|
||||
/* If Geo is enabled, don't bother. */
|
||||
if ( !\IPS\Settings::i()->ipsgeoip )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* See we have anything configured. If we don't, don't bother. */
|
||||
$settings = \IPS\Settings::i()->spam_geo_settings ? json_decode( \IPS\Settings::i()->spam_geo_settings, true ) : array();
|
||||
|
||||
if ( !\count( $settings ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$location = \IPS\GeoLocation::getRequesterLocation();
|
||||
}
|
||||
catch( \BadMethodCallException | \IPS\Http\Request\Exception | \RuntimeException | \OutOfRangeException $e )
|
||||
{
|
||||
/* If it fails for any reason, don't bother. */
|
||||
\IPS\Log::debug( $e, 'spam-service' );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* All good, start checking. */
|
||||
if ( !isset( $settings[ $location->country ] ) )
|
||||
{
|
||||
/* Not in the list, we're good. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* See what action we're taking. */
|
||||
switch( $settings[$location->country] )
|
||||
{
|
||||
case 'moderate':
|
||||
\IPS\Settings::i()->reg_auth_type = 'admin';
|
||||
$geoBlock = TRUE;
|
||||
return 2;
|
||||
|
||||
case 'block':
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for disposable email address domain
|
||||
*
|
||||
* @param string $emailAddress Email to check, or NULL for current.
|
||||
* @param bool $disposable If set, will modify by reference if the user used a disposable email.
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function disposableEmailCheck( $emailAddress, &$disposable=FALSE ): ?int
|
||||
{
|
||||
$email = $emailAddress ?: $this->email;
|
||||
|
||||
try
|
||||
{
|
||||
$response = \IPS\Http\Url::ips( 'spam/disposable' )->request()->login( \IPS\Settings::i()->ipb_reg_number, '' )->post( array(
|
||||
'email' => $email,
|
||||
) );
|
||||
|
||||
if ( $response->httpResponseCode != 200 )
|
||||
{
|
||||
throw new \DomainException( print_r( $response, TRUE ) );
|
||||
}
|
||||
|
||||
$result = (bool) $response->decodeJson()['result'];
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
\IPS\Log::debug( $e, 'spam-service' );
|
||||
$result = false;
|
||||
}
|
||||
|
||||
$action = NULL;
|
||||
if ( $result )
|
||||
{
|
||||
$disposable = TRUE;
|
||||
|
||||
$action = \IPS\Settings::i()->spam_service_disposable;
|
||||
|
||||
/* Perform Action */
|
||||
switch( $action )
|
||||
{
|
||||
/* Proceed with registration */
|
||||
case 1:
|
||||
break;
|
||||
|
||||
/* Flag for admin approval */
|
||||
case 2:
|
||||
\IPS\Settings::i()->reg_auth_type = 'admin';
|
||||
break;
|
||||
|
||||
/* Approve the account, but ban it */
|
||||
case 3:
|
||||
$this->temp_ban = -1;
|
||||
$this->members_bitoptions['bw_is_spammer'] = TRUE;
|
||||
break;
|
||||
|
||||
/* Deny registration - we return the code and the controller is expected to show an error */
|
||||
case 4:
|
||||
break;
|
||||
|
||||
/* Moderate posts */
|
||||
case 5:
|
||||
$days = json_decode( \IPS\Settings::i()->spam_service_days, TRUE );
|
||||
|
||||
if ( isset( $days['disposable'] ) )
|
||||
{
|
||||
if ( $days['disposable'] == -1 )
|
||||
{
|
||||
$this->mod_posts = -1;
|
||||
}
|
||||
else if ( $days['disposable'] > 0 )
|
||||
{
|
||||
$this->mod_posts = \IPS\DateTime::ts( time() )->add( new \DateInterval( "P{$days['disposable']}D" ) )->getTimestamp();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Sync
|
||||
*
|
||||
@@ -4597,6 +4826,8 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
/* Reset the flag */
|
||||
$this->members_bitoptions['validating'] = FALSE;
|
||||
$this->save();
|
||||
|
||||
\IPS\Api\Webhook::fire( 'member_registration_complete', $this, $this->webhookFilters() );
|
||||
|
||||
/* Sync */
|
||||
$this->memberSync( 'onValidate' );
|
||||
@@ -5931,7 +6162,7 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
foreach( $members as $data )
|
||||
{
|
||||
list( $type, $memberId ) = explode( '-', $data );
|
||||
[ $type, $memberId ] = explode( '-', $data );
|
||||
$insertId = \IPS\Db::i()->insert( 'core_achievements_log_milestones', [
|
||||
'milestone_member_id' => $memberId,
|
||||
'milestone_rule' => $ruleId,
|
||||
@@ -6190,4 +6421,95 @@ class _Member extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the member's personal information
|
||||
*
|
||||
* @return \IPS\Xml\SimpleXML
|
||||
*/
|
||||
public function getPiiData(): \IPS\Xml\SimpleXML
|
||||
{
|
||||
/* Init */
|
||||
$xml = \IPS\Xml\SimpleXML::create('member_export');
|
||||
$xml->addAttribute( 'created', \IPS\DateTime::ts( time() )->rfc3339() );
|
||||
|
||||
/* Get the data */
|
||||
foreach( \IPS\Application::allExtensions( 'core', 'MemberExportPersonalInformation', TRUE, 'core' ) AS $key => $ext )
|
||||
{
|
||||
if ( $data = $ext->getData( $this ) and \is_array( $data ) and \count( $data ) )
|
||||
{
|
||||
$child = $xml->addChild( $key );
|
||||
foreach( $data as $k => $v )
|
||||
{
|
||||
$child->addChild( $k, $v );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there a deletion request pending?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_isDeletionPending(): bool
|
||||
{
|
||||
$where = [];
|
||||
$where[] = ['member_id=?', $this->member_id];
|
||||
$where[] = [ \IPS\Db::i()->in( 'action',[\IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE, \IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE_VALIDATION ] ) ];
|
||||
|
||||
return !\IPS\Db::i()->select( 'count(*)', \IPS\Member\PrivacyAction::$databaseTable, $where )->first() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all logged IP addresses which were logged after a certain time
|
||||
*
|
||||
* @param int $time
|
||||
* @return void
|
||||
*/
|
||||
public static function pruneAllLoggedIpAddresses(int $time )
|
||||
{
|
||||
foreach ( \IPS\Content::routedClasses( FALSE, TRUE ) as $class )
|
||||
{
|
||||
try
|
||||
{
|
||||
$class::pruneIpAddresses( \IPS\Settings::i()->ip_address_prune );
|
||||
}
|
||||
catch( \Exception $ex ) { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* PMs and Ratings are treated extra */
|
||||
\IPS\Db::i()->update( 'core_message_posts', array( 'msg_ip_address' => '' ), array( "msg_ip_address != '' AND msg_date <= " . $time ) );
|
||||
\IPS\Db::i()->update( 'core_ratings', array( 'ip' => '' ), array( "ip != '' AND rating_date IS NOT NULL AND rating_date <= " . $time ) );
|
||||
|
||||
foreach ( \IPS\Application::allExtensions( 'core', 'IpAddresses', FALSE, 'core' ) as $key => $extension )
|
||||
{
|
||||
if( method_exists( $extension, 'pruneIpAddress'))
|
||||
{
|
||||
$extension->pruneIpAddress( $time );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can this member request that his account gets deleted via the frontend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canUseAccountDeletion(): bool
|
||||
{
|
||||
if( \IPS\Settings::i()->right_to_be_forgotten_type != 'on' )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( \IPS\Member::loggedIn()->isAdmin() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Privacy Action
|
||||
* @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 07 March 2023
|
||||
*/
|
||||
|
||||
namespace IPS\Member;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* PrivacyAction Model
|
||||
*/
|
||||
class _PrivacyAction extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'core_member_privacy_actions';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = [];
|
||||
|
||||
/**
|
||||
* @brief PII request
|
||||
*/
|
||||
const TYPE_REQUEST_PII = 'pii_download';
|
||||
|
||||
/**
|
||||
* @brief Account deletion request
|
||||
*/
|
||||
const TYPE_REQUEST_DELETE = 'delete_account';
|
||||
|
||||
/**
|
||||
* @brief Account deletion requires validation
|
||||
*/
|
||||
const TYPE_REQUEST_DELETE_VALIDATION = 'delete_account_validation';
|
||||
|
||||
/**
|
||||
* Request the PII Data
|
||||
*
|
||||
* @param \IPS\Member|NULL $member
|
||||
* @return void
|
||||
*/
|
||||
public static function requestPiiData( \IPS\Member $member = NULL )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
$obj = new static;
|
||||
$obj->member_id = $member->member_id;
|
||||
$obj->request_date = time();
|
||||
$obj->action = static::TYPE_REQUEST_PII;
|
||||
$obj->save();
|
||||
|
||||
\IPS\core\AdminNotification::send( 'core', 'PiiDataRequest', NULL, TRUE, $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the member request his PII Data?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member
|
||||
* @return bool
|
||||
*/
|
||||
public static function canRequestPiiData( \IPS\Member $member = NULL ): bool
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
if ( $member->member_id AND !static::hasPiiRequest($member) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the member download his PII Data?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member
|
||||
* @return bool
|
||||
*/
|
||||
public static function canDownloadPiiData( \IPS\Member $member = NULL ): bool
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
if( !$member->member_id )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( \IPS\Db::i()->select( 'count(*)', static::$databaseTable, [ 'member_id=? AND action=? AND approved=?', $member->member_id, static::TYPE_REQUEST_PII, 1 ] )->first() > 0 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the deletion request by member and key
|
||||
*
|
||||
* @param \IPS\Member $member
|
||||
* @param string $key
|
||||
* @return static
|
||||
* @throws \OutOfRangeException
|
||||
*/
|
||||
public static function getDeletionRequestByMemberAndKey( \IPS\Member $member, string $key ): static
|
||||
{
|
||||
try
|
||||
{
|
||||
$where = [];
|
||||
$where[] = ['member_id=?', $member->member_id];
|
||||
$where[] = ['vkey=?', $key];
|
||||
$where[] = [ \IPS\Db::i()->in( 'action',[static::TYPE_REQUEST_DELETE, static::TYPE_REQUEST_DELETE_VALIDATION ] )];
|
||||
return static::constructFromData(\IPS\Db::i()->select( '*', static::$databaseTable, $where )->first() );
|
||||
}
|
||||
catch( \UnderflowException $e ){
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a PII Data Request pending for this member?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member
|
||||
* @param bool $approved
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasPiiRequest( \IPS\Member $member = NULL, bool $approved = FALSE ): bool
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->select( '*', static::$databaseTable, [ 'member_id=? AND action=? AND approved=?', $member->member_id, static::TYPE_REQUEST_PII, (int) $approved ] )->first();
|
||||
return TRUE;
|
||||
}
|
||||
catch( \UnderflowException $e ){}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve the PII Request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function approvePiiRequest()
|
||||
{
|
||||
$this->approved = TRUE;
|
||||
$this->save();
|
||||
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'pii_data', $this, array( $this ) );
|
||||
$notification->recipients->attach( $this->member );
|
||||
$notification->send();
|
||||
static::resetPiiAcpNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject the PII Data Request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rejectPiiRequest()
|
||||
{
|
||||
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'pii_data_rejected', $this, array( $this ) );
|
||||
$notification->recipients->attach( $this->member );
|
||||
$notification->send();
|
||||
$this->delete();
|
||||
static::resetPiiAcpNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the member object
|
||||
*
|
||||
* @return \IPS\Member
|
||||
*/
|
||||
public function get_member(): \IPS\Member
|
||||
{
|
||||
return \IPS\Member::load( $this->member_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the PII request ACP notifications
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function resetPiiAcpNotifications()
|
||||
{
|
||||
if ( !\IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, array( 'action=? AND approved=?', static::TYPE_REQUEST_PII, 0 ) )->first() )
|
||||
{
|
||||
\IPS\core\AdminNotification::remove( 'core', 'PiiDataRequest' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the account deletion ACP notifications
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function resetDeletionAcpNotifications()
|
||||
{
|
||||
if ( !\IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, array( 'action=?', static::TYPE_REQUEST_DELETE ) )->first() )
|
||||
{
|
||||
\IPS\core\AdminNotification::remove( 'core', 'AccountDeletion' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current member request account deletion?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member
|
||||
* @return bool
|
||||
*/
|
||||
public static function canDeleteAccount( \IPS\Member $member = NULL ): bool
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
return \IPS\Db::i()->select( 'count(*)', static::$databaseTable, [ 'member_id=? AND action=?', $member->member_id, static::TYPE_REQUEST_DELETE ] )->first() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and log the account deletion request
|
||||
*
|
||||
* @param \IPS\Member|NULL $member
|
||||
* @return void
|
||||
*/
|
||||
public static function requestAccountDeletion( \IPS\Member $member = NULL )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
$obj = new static;
|
||||
$obj->member_id = $member->member_id;
|
||||
$obj->request_date = time();
|
||||
$obj->action = static::TYPE_REQUEST_DELETE_VALIDATION;
|
||||
$vkey = md5( $member->members_pass_hash . \IPS\Login::generateRandomString() );
|
||||
$obj->vkey = $vkey;
|
||||
\IPS\Email::buildFromTemplate( 'core', 'account_deletion_confirmation', array( $member, $vkey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
|
||||
$obj->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the account deletion request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function confirmAccountDeletion()
|
||||
{
|
||||
$this->action = static::TYPE_REQUEST_DELETE;
|
||||
\IPS\core\AdminNotification::send( 'core', 'AccountDeletion', NULL, TRUE, $this->member );
|
||||
$this->member->logHistory( 'core', 'privacy', array( 'type' => 'account_deletion_requested' ) );
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the account
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAccount()
|
||||
{
|
||||
/** @var \IPS\Member $member */
|
||||
$member = $this->member;
|
||||
|
||||
$member->delete( TRUE, FALSE );
|
||||
static::resetDeletionAcpNotifications();
|
||||
\IPS\Session::i()->log( 'acplog__members_deleted_id', array( $member->name => FALSE, $member->member_id => FALSE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject the account deletion request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rejectDeletionRequest()
|
||||
{
|
||||
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'account_del_request_rejected', $this, array( $this ) );
|
||||
$notification->recipients->attach( $this->member );
|
||||
$notification->send();
|
||||
$this->delete();
|
||||
static::resetDeletionAcpNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all ACP notifications and caches if there are any, should be called after a member was deleted or merged
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function resetAcpNotificationCounts()
|
||||
{
|
||||
static::resetPiiAcpNotifications();
|
||||
static::resetDeletionAcpNotifications();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user