Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

+77 -70
View File
@@ -11,31 +11,45 @@
namespace IPS\Member;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Application;
use IPS\core\AdminNotification;
use IPS\Db;
use IPS\Email;
use IPS\Login;
use IPS\Member;
use IPS\Notification;
use IPS\Patterns\ActiveRecord;
use IPS\Session;
use OutOfRangeException;
use UnderflowException;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PrivacyAction Model
*/
class _PrivacyAction extends \IPS\Patterns\ActiveRecord
class PrivacyAction extends ActiveRecord
{
/**
* @brief [ActiveRecord] Multiton Store
*/
protected static $multitons;
protected static array $multitons;
/**
* @brief [ActiveRecord] Database Table
*/
public static $databaseTable = 'core_member_privacy_actions';
public static ?string $databaseTable = 'core_member_privacy_actions';
/**
* @brief [ActiveRecord] Multiton Map
*/
protected static $multitonMap = [];
protected static array $multitonMap = [];
/**
* @brief PII request
@@ -55,30 +69,30 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
/**
* Request the PII Data
*
* @param \IPS\Member|NULL $member
* @param Member|null $member
* @return void
*/
public static function requestPiiData( \IPS\Member $member = NULL )
public static function requestPiiData( Member $member = NULL ) : void
{
$member = $member ?: \IPS\Member::loggedIn();
$member = $member ?: 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 );
AdminNotification::send( 'core', 'PiiDataRequest', NULL, TRUE, $member );
}
/**
* Can the member request his PII Data?
*
* @param \IPS\Member|NULL $member
* @param Member|null $member
* @return bool
*/
public static function canRequestPiiData( \IPS\Member $member = NULL ): bool
public static function canRequestPiiData( Member $member = NULL ): bool
{
$member = $member ?: \IPS\Member::loggedIn();
$member = $member ?: Member::loggedIn();
if ( $member->member_id AND !static::hasPiiRequest($member) )
{
return TRUE;
@@ -90,18 +104,18 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
/**
* Can the member download his PII Data?
*
* @param \IPS\Member|NULL $member
* @param Member|null $member
* @return bool
*/
public static function canDownloadPiiData( \IPS\Member $member = NULL ): bool
public static function canDownloadPiiData( Member $member = NULL ): bool
{
$member = $member ?: \IPS\Member::loggedIn();
$member = $member ?: 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 )
if ( Db::i()->select( 'count(*)', static::$databaseTable, [ 'member_id=? AND action=? AND approved=?', $member->member_id, static::TYPE_REQUEST_PII, 1 ] )->first() > 0 )
{
return TRUE;
}
@@ -112,42 +126,44 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
/**
* Get the deletion request by member and key
*
* @param \IPS\Member $member
* @param Member $member
* @param string $key
* @throws OutOfRangeException
* @return static
* @throws \OutOfRangeException
*/
public static function getDeletionRequestByMemberAndKey( \IPS\Member $member, string $key ): static
public static function getDeletionRequestByMemberAndKey( 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() );
$where[] = [ Db::i()->in( 'action',[static::TYPE_REQUEST_DELETE, static::TYPE_REQUEST_DELETE_VALIDATION ] )];
return static::constructFromData( Db::i()->select( '*', static::$databaseTable, $where )->first() );
}
catch( \UnderflowException $e ){
throw new \OutOfRangeException;
catch( UnderflowException $e )
{
throw new OutOfRangeException;
}
}
/**
* Is a PII Data Request pending for this member?
*
* @param \IPS\Member|NULL $member
* @param Member|null $member
* @param bool $approved
* @return bool
*/
public static function hasPiiRequest( \IPS\Member $member = NULL, bool $approved = FALSE ): bool
public static function hasPiiRequest( Member $member = NULL, bool $approved = FALSE ): bool
{
$member = $member ?: \IPS\Member::loggedIn();
$member = $member ?: 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();
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 ){}
catch( UnderflowException $e ){}
return FALSE;
}
@@ -156,11 +172,11 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
*
* @return void
*/
public function approvePiiRequest()
public function approvePiiRequest() : void
{
$this->approved = TRUE;
$this->save();
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'pii_data', $this, array( $this ) );
$notification = new Notification( Application::load( 'core' ), 'pii_data', $this, array( $this ) );
$notification->recipients->attach( $this->member );
$notification->send();
static::resetPiiAcpNotifications();
@@ -171,9 +187,9 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
*
* @return void
*/
public function rejectPiiRequest()
public function rejectPiiRequest() : void
{
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'pii_data_rejected', $this, array( $this ) );
$notification = new Notification( Application::load( 'core' ), 'pii_data_rejected', $this, array( $this ) );
$notification->recipients->attach( $this->member );
$notification->send();
$this->delete();
@@ -183,11 +199,11 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
/**
* Get the member object
*
* @return \IPS\Member
* @return Member
*/
public function get_member(): \IPS\Member
public function get_member(): Member
{
return \IPS\Member::load( $this->member_id );
return Member::load( $this->member_id );
}
/**
@@ -195,11 +211,11 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
*
* @return void
*/
public static function resetPiiAcpNotifications()
public static function resetPiiAcpNotifications() : void
{
if ( !\IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, array( 'action=? AND approved=?', static::TYPE_REQUEST_PII, 0 ) )->first() )
if ( !Db::i()->select( 'COUNT(*)', static::$databaseTable, array( 'action=? AND approved=?', static::TYPE_REQUEST_PII, 0 ) )->first() )
{
\IPS\core\AdminNotification::remove( 'core', 'PiiDataRequest' );
AdminNotification::remove( 'core', 'PiiDataRequest' );
}
}
@@ -208,43 +224,45 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
*
* @return void
*/
public static function resetDeletionAcpNotifications()
public static function resetDeletionAcpNotifications() : void
{
if ( !\IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, array( 'action=?', static::TYPE_REQUEST_DELETE ) )->first() )
if ( !Db::i()->select( 'COUNT(*)', static::$databaseTable, array( 'action=?', static::TYPE_REQUEST_DELETE ) )->first() )
{
\IPS\core\AdminNotification::remove( 'core', 'AccountDeletion' );
AdminNotification::remove( 'core', 'AccountDeletion' );
}
}
/**
* Can the current member request account deletion?
*
* @param \IPS\Member|NULL $member
* @param Member|null $member
* @return bool
*/
public static function canDeleteAccount( \IPS\Member $member = NULL ): bool
public static function canDeleteAccount( 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;
$member = $member ?: Member::loggedIn();
return Db::i()->select( 'count(*)', static::$databaseTable, [
[ 'member_id=?', $member->member_id ],
[ Db::i()->in( 'action', [ static::TYPE_REQUEST_DELETE, static::TYPE_REQUEST_DELETE_VALIDATION ] ) ]
] )->first() == 0;
}
/**
* Create and log the account deletion request
*
* @param \IPS\Member|NULL $member
* @param Member|null $member
* @return void
*/
public static function requestAccountDeletion( \IPS\Member $member = NULL )
public static function requestAccountDeletion( Member $member = NULL ) : void
{
$member = $member ?: \IPS\Member::loggedIn();
$member = $member ?: 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() );
$vkey = md5( $member->members_pass_hash . Login::generateRandomString() );
$obj->vkey = $vkey;
\IPS\Email::buildFromTemplate( 'core', 'account_deletion_confirmation', array( $member, $vkey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
Email::buildFromTemplate( 'core', 'account_deletion_confirmation', array( $member, $vkey ), Email::TYPE_TRANSACTIONAL )->send( $member );
$obj->save();
}
@@ -254,10 +272,10 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
*
* @return void
*/
public function confirmAccountDeletion()
public function confirmAccountDeletion() : void
{
$this->action = static::TYPE_REQUEST_DELETE;
\IPS\core\AdminNotification::send( 'core', 'AccountDeletion', NULL, TRUE, $this->member );
AdminNotification::send( 'core', 'AccountDeletion', NULL, TRUE, $this->member );
$this->member->logHistory( 'core', 'privacy', array( 'type' => 'account_deletion_requested' ) );
$this->save();
}
@@ -267,14 +285,14 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
*
* @return void
*/
public function deleteAccount()
public function deleteAccount() : void
{
/** @var \IPS\Member $member */
/** @var 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 ) );
Session::i()->log( 'acplog__members_deleted_id', array( $member->name => FALSE, $member->member_id => FALSE ) );
}
/**
@@ -282,23 +300,12 @@ class _PrivacyAction extends \IPS\Patterns\ActiveRecord
*
* @return void
*/
public function rejectDeletionRequest()
public function rejectDeletionRequest() : void
{
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'account_del_request_rejected', $this, array( $this ) );
$notification = new Notification( 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();
}
}