Version 4.3.6

This commit is contained in:
Neo committed 2025-12-19 05:49:12 -08:00
1 parent fe1acf984a
commit 1a0c7fd3c2
609 files changed
+14707 -5726

No files matched your search

+15 -3
View File
@@ -359,7 +359,15 @@ class _Club extends \IPS\Patterns\ActiveRecord implements \IPS\Content\Embeddabl
}
\IPS\Member::loggedIn()->language()->words['club_fee_desc'] = $commissionBlurb;
$form->add( new \IPS\nexus\Form\Money( 'club_fee', $this->id ? json_decode( $this->fee, TRUE ) : array(), NULL, array(), NULL, NULL, NULL, 'club_fee' ) );
$form->add( new \IPS\nexus\Form\Money( 'club_fee', $this->id ? json_decode( $this->fee, TRUE ) : array(), NULL, array(), function( $value ){
foreach( $value as $currency => $fee )
{
if( !$fee->amount->isGreaterThanZero() )
{
throw new \DomainException( 'form_required' );
}
}
}, NULL, NULL, 'club_fee' ) );
$form->add( new \IPS\Helpers\Form\Radio( 'club_renewals', $this->id ? ( $this->renewal_term ? 1 : 0 ) : 0, TRUE, array(
'options' => array( 0 => 'club_renewals_off', 1 => 'club_renewals_on' ),
'toggles' => array( 1 => array( 'club_renewal_term' ) )
@@ -696,7 +704,7 @@ class _Club extends \IPS\Patterns\ActiveRecord implements \IPS\Content\Embeddabl
}
/**
* Joining fee
* Renewal fee
*
* @param string|NULL $currency Desired currency, or NULL to choose based on member's chosen currency
* @return \IPS\nexus\Money|NULL
@@ -740,7 +748,11 @@ class _Club extends \IPS\Patterns\ActiveRecord implements \IPS\Content\Embeddabl
try
{
$renewalTerm = $this->renewalTerm( $fee->currency );
if ( $renewalTerm->cost->amount == $fee->amount )
if ( !$renewalTerm )
{
return $fee;
}
else if ( $renewalTerm AND $renewalTerm->cost->amount == $fee->amount )
{
return $renewalTerm;
}
+20 -16
View File
@@ -150,16 +150,17 @@ class _Device extends \IPS\Patterns\ActiveRecord
$object->device_key = $deviceKey;
return $object;
}
/**
* Update after logging in / automatic authentication with current request's user agent / IP address, etc.
*
* @param bool|null $rememberMe Remember me? NULL can be provided if the login is being processed from somewhere that doesn't ask
* @param \IPS\Login\Handler $loginHandler The login handler which processed the login, or NULL if updating an existing login. Can also be empty string for logins that weren't processed by any handler (such as after registration or using the lost password feature)
* @param bool $refreshLoginKey If login key should be refreshed (FALSE for automatic logins which will need the same login key subsequently)
* @param book $setCookies Should the cookies be set ( FALSE for ACP login )
* @return void
*/
public function updateAfterAuthentication( $rememberMe, \IPS\Login\Handler $loginHandler=NULL, $refreshLoginKey=TRUE )
public function updateAfterAuthentication( $rememberMe, \IPS\Login\Handler $loginHandler=NULL, $refreshLoginKey=TRUE, $setCookies = TRUE )
{
$this->user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : NULL;
if ( $refreshLoginKey )
@@ -174,21 +175,24 @@ class _Device extends \IPS\Patterns\ActiveRecord
$this->save();
$this->logIpAddress( \IPS\Request::i()->ipAddress() );
$cookieExpiration = ( new \IPS\DateTime )->add( new \DateInterval( static::LOGIN_KEY_VALIDITY ) );
if ( $rememberMe === NULL )
if ( $setCookies )
{
\IPS\Request::i()->setCookie( 'member_id', $this->member_id, $cookieExpiration );
}
elseif ( $rememberMe === TRUE )
{
\IPS\Request::i()->setCookie( 'member_id', $this->member_id, $cookieExpiration );
\IPS\Request::i()->setCookie( 'login_key', $this->login_key, $cookieExpiration );
}
else
{
\IPS\Request::i()->setCookie( 'member_id', $this->member_id, NULL ); // Just tells the guest caching mechanism that we are logged in, so it can expire on the session end
\IPS\Request::i()->setCookie( 'login_key', NULL ); // Clear it in case they previously had chosen "Remember Me"
$cookieExpiration = ( new \IPS\DateTime )->add( new \DateInterval( static::LOGIN_KEY_VALIDITY ) );
if ( $rememberMe === NULL )
{
\IPS\Request::i()->setCookie( 'member_id', $this->member_id, $cookieExpiration );
}
elseif ( $rememberMe === TRUE )
{
\IPS\Request::i()->setCookie( 'member_id', $this->member_id, $cookieExpiration );
\IPS\Request::i()->setCookie( 'login_key', $this->login_key, $cookieExpiration );
}
else
{
\IPS\Request::i()->setCookie( 'member_id', $this->member_id, NULL ); // Just tells the guest caching mechanism that we are logged in, so it can expire on the session end
\IPS\Request::i()->setCookie( 'login_key', NULL ); // Clear it in case they previously had chosen "Remember Me"
}
}
}
+1 -1
View File
@@ -61,7 +61,7 @@ class _History extends \IPS\Helpers\Table\Db
}
/* This is here specifically so that log_type is always shown first, if required */
$this->include = array_merge( $this->include, array( 'log_id', 'log_date', 'log_data' ) );
$this->include = array_merge( $this->include, array( 'log_date', 'log_data' ) );
if ( $showIp )
{
+95 -14
View File
@@ -106,8 +106,8 @@ class _Member extends \IPS\Patterns\ActiveRecord
'security_questions_opt_out' => 1024,
'ignore_promotions' => 2048,// 1 means that the user will not be checked against group promotion rules. Set by Commerce to prevent "promoting" users incorrectly after a purchase.
'profile_completed' => 4096,
'profile_completion_dismissed' => 8192
'profile_completion_dismissed' => 8192,
'created_externally' => 16384
)
)
);
@@ -153,6 +153,12 @@ class _Member extends \IPS\Patterns\ActiveRecord
if ( isset( \IPS\Data\Store::i()->$key ) )
{
static::$loggedInMember = static::load( \IPS\Data\Store::i()->$key );
if ( !static::$loggedInMember->member_id )
{
unset( \IPS\Data\Store::i()->$key );
unset( $_SESSION['logged_in_as_key'] );
}
}
}
}
@@ -565,12 +571,14 @@ class _Member extends \IPS\Patterns\ActiveRecord
{
if ( isset( $class::$databaseColumnMap['author'] ) and ( $action == 'delete' or in_array( 'IPS\Content\Hideable', class_implements( $class ) ) ) )
{
\IPS\Task::queue( 'core', 'MemberContent', array_merge( array( 'initiated_by_member_id' => \IPS\Member::loggedIn()->member_id, 'member_id' => $this->member_id, 'name' => $this->name, 'class' => $class, 'action' => $action ), $extra ), 1 );
/* Comments run first so rebuilding topic doesn't fail with incorrect author ID */
$order = ( is_subclass_of( $class, '\IPS\Content\Comment' ) ) ? 1 : 2;
\IPS\Task::queue( 'core', 'MemberContent', array_merge( array( 'initiated_by_member_id' => \IPS\Member::loggedIn()->member_id, 'member_id' => $this->member_id, 'name' => $this->name, 'class' => $class, 'action' => $action ), $extra ), $order );
}
}
/* And private messages */
\IPS\Task::queue( 'core', 'MemberContent', array_merge( array( 'initiated_by_member_id' => \IPS\Member::loggedIn()->member_id, 'member_id' => $this->member_id, 'name' => $this->name, 'class' => 'IPS\\core\\Messenger\\Conversation', 'action' => $action ), $extra ), 1 );
\IPS\Task::queue( 'core', 'MemberContent', array_merge( array( 'initiated_by_member_id' => \IPS\Member::loggedIn()->member_id, 'member_id' => $this->member_id, 'name' => $this->name, 'class' => 'IPS\\core\\Messenger\\Conversation', 'action' => $action ), $extra ), 2 );
}
/**
@@ -1601,7 +1609,18 @@ class _Member extends \IPS\Patterns\ActiveRecord
if ( isset( $memberData['member_id'] ) and $memberData['member_id'] )
{
/* Is Gravatar disabled for them? */
$gravatarDisabled = isset( $memberData['members_bitoptions'] ) AND is_object( $memberData['members_bitoptions'] ) ? $memberData['members_bitoptions']['bw_disable_gravatar'] : $memberData['members_bitoptions'] & static::$bitOptions['members_bitoptions']['members_bitoptions']['bw_disable_gravatar'];
$gravatarDisabled = FALSE;
if ( isset( $memberData['members_bitoptions'] ) )
{
if ( is_object( $memberData['members_bitoptions'] ) )
{
$gravatarDisabled = $memberData['members_bitoptions']['bw_disable_gravatar'];
}
else
{
$gravatarDisabled = $memberData['members_bitoptions'] & static::$bitOptions['members_bitoptions']['members_bitoptions']['bw_disable_gravatar'];
}
}
/* Either uploaded or synced from social media */
if ( $memberData['pp_main_photo'] and ( mb_substr( $memberData['pp_photo_type'], 0, 5 ) === 'sync-' or $memberData['pp_photo_type'] === 'custom' or ( \IPS\Settings::i()->letter_photos == 'letters' AND $memberData['pp_photo_type'] == 'letter' and $useDefaultPhoto and ( $gravatarDisabled OR !\IPS\Settings::i()->allow_gravatars ) ) ) )
@@ -1830,6 +1849,31 @@ class _Member extends \IPS\Patterns\ActiveRecord
return TRUE;
}
/**
* Is Online Anonymously
*
* @return bool
*/
public function isOnlineAnonymously()
{
if ( !$this->member_id )
{
return FALSE;
}
if ( $this->sessionData === NULL )
{
$this->sessionData = \IPS\Session\Store::i()->getLatestMemberSession( $this->member_id );
}
if ( $this->sessionData === FALSE )
{
return FALSE;
}
return ( $this->sessionData['login_type'] == \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS );
}
/**
* Is banned?
* If is banned until a certain time, returns an \IPS\DateTime object
@@ -2852,7 +2896,7 @@ class _Member extends \IPS\Patterns\ActiveRecord
$reportCount = \IPS\Db::i()->select(
'COUNT(*)',
'core_rc_index',
array( $where, \IPS\Db::i()->select( 'perm_id', 'core_permission_index', \IPS\Db::i()->findInSet( 'perm_view', array_merge( array( \IPS\Member::loggedIn()->member_group_id ), array_filter( explode( ',', \IPS\Member::loggedIn()->mgroup_others ) ) ) ) . " OR perm_view='*'" ) )
array( $where, \IPS\Db::i()->select( 'perm_id', 'core_permission_index', \IPS\Db::i()->findInSet( 'perm_view', $this->permissionArray() ) . " OR perm_view='*'" ) )
)->first();
if( $force === FALSE )
@@ -3821,10 +3865,12 @@ class _Member extends \IPS\Patterns\ActiveRecord
try
{
$group = \IPS\Member\Group::load( $action['primary_group'] );
$this->member_group_id = $action['primary_group'];
/* Need to store the history before we actually change the group, otherwise history shows "promoted from group X to X" */
$this->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'promotion', 'id' => $ruleToUse->id, 'old' => $this->member_group_id, 'new' => $action['primary_group'] ) );
$this->member_group_id = $action['primary_group'];
/* Reset profile completion flag to see if there are any items to complete now permissions are elevated */
$this->members_bitoptions['profile_completed'] = 0;
}
@@ -4106,7 +4152,7 @@ class _Member extends \IPS\Patterns\ActiveRecord
/**
* Email Validation Confirmed
*
* @param $record validating record
* @param array $record validating record
* @return void
*/
public function emailValidationConfirmed( $record )
@@ -4139,7 +4185,6 @@ class _Member extends \IPS\Patterns\ActiveRecord
* If email-only validation is enabled: this is called after the user has validated their email address or if the admin manually validates the account
* If admin (including email and admin) validation is enabled: this is called after the admin has validated the account
*
* @param array $record Row from core_validating
* @return void
*/
public function validationComplete()
@@ -4265,7 +4310,7 @@ class _Member extends \IPS\Patterns\ActiveRecord
{
case 'email':
$email = $method->userEmail( $this );
if ( $email != $this->email )
if ( $email and $email != $this->email )
{
if ( $error = \IPS\Login::emailIsInUse( $email, $this ) )
{
@@ -4347,7 +4392,7 @@ class _Member extends \IPS\Patterns\ActiveRecord
$contents = $photoUrl->request()->get();
$md5 = md5( $contents );
if ( !isset( $v['ref'] ) or $md5 != $v['ref'] )
if ( $contents AND ( !isset( $v['ref'] ) or $md5 != $v['ref'] ) )
{
$photoVars = explode( ':', $this->group['g_photo_max_vars'] );
@@ -4413,7 +4458,7 @@ class _Member extends \IPS\Patterns\ActiveRecord
throw new \DomainException('upload_too_big_unspecific');
}
$newFile = \IPS\File::create( 'core_Profile', basename( $coverPhotoUrl->data[ \IPS\Http\Url::COMPONENT_PATH ] ), (string) $image );
$newFile = \IPS\File::create( 'core_Profile', 'imported-cover-photo-' . $this->member_id . '.' . $image->type, (string) $image );
if ( $this->pp_cover_photo )
{
@@ -4424,7 +4469,7 @@ class _Member extends \IPS\Patterns\ActiveRecord
catch ( \Exception $e ) { }
}
$this->pp_cover_photo = $newFile;
$this->pp_cover_photo = (string) $newFile;
$this->logHistory( 'core', 'photo', array( 'action' => 'new', 'type' => 'profilesync', 'id' => $method->id, 'service' => $method::getTitle() ) );
$profileSync['cover']['ref'] = $md5;
@@ -4737,6 +4782,10 @@ class _Member extends \IPS\Patterns\ActiveRecord
{
return new \IPS\Math\Number( '100' );
}
elseif( !$total )
{
return new \IPS\Math\Number( '100' );
}
else
{
return new \IPS\Math\Number( (string) round( 100 / $total * $done ) );
@@ -4827,4 +4876,36 @@ class _Member extends \IPS\Patterns\ActiveRecord
{
return $this->staticCoverPhotoBackgroundColor( $this->name );
}
/**
* returns the recent profile visitors
*
* @return array
*/
public function get_profileVisitors()
{
$visitors = array();
$visitorData = array();
$visitorInfo = json_decode( $this->pp_last_visitors, TRUE );
if ( !is_array( $visitorInfo ) )
{
$visitorInfo = array();
}
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members', array( \IPS\Db::i()->in( 'member_id', array_keys( array_reverse( $visitorInfo, TRUE ) ) ) ) ), 'IPS\Member' ) AS $row )
{
$visitorData[$row->member_id] = $row;
}
foreach( array_reverse( $visitorInfo, TRUE ) as $id => $time )
{
if ( isset( $visitorData[$id] ) )
{
$visitors[$id]['member'] = $visitorData[$id];
$visitors[$id]['visit_time'] = $time;
}
}
return $visitors;
}
}
+53 -3
View File
@@ -428,13 +428,16 @@ class _ProfileStep extends \IPS\Node\Model
{
if ( isset( $subActions[ $item ] ) )
{
$lang[] = \IPS\Member::loggedIn()->language()->get( $subActions[ $item ] );
$lang[] = \IPS\Member::loggedIn()->language()->addToStack( $subActions[ $item ] );
}
}
if ( count( $lang ) )
{
return \IPS\Member::loggedIn()->language()->get( 'complete_profile_' . $this->completion_act ) . ' (' . \IPS\Member::loggedIn()->language()->formatList( $lang ) . ')';
$result = \IPS\Member::loggedIn()->language()->get( 'complete_profile_' . $this->completion_act ) . ' (' . \IPS\Member::loggedIn()->language()->formatList( $lang ) . ')';
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $result );
return $result;
}
else
{
@@ -466,6 +469,46 @@ class _ProfileStep extends \IPS\Node\Model
return $this->extension->completed( $this, $member );
}
/**
* @brief Can add cache
*/
protected static $canAdd = NULL;
/**
* Can add new steps
*
* @return param
*/
public function canAdd()
{
if ( static::$canAdd === NULL )
{
$actions = static::actions();
$subActions = static::subActions();
$usedSubActions = $this->usedSubActions();
foreach( $actions as $key => $lang )
{
if ( isset( $subActions[ $key ] ) )
{
if ( isset( $usedSubActions[ $key ] ) )
{
if ( \count( $usedSubActions[ $key ] ) == \count( $subActions[ $key ] ) )
{
/* Nothing left to select, so skip this */
unset( $actions[ $key ] );
continue;
}
}
}
}
static::$canAdd = \count( $actions );
}
return static::$canAdd;
}
/**
* Form
*
@@ -491,6 +534,13 @@ class _ProfileStep extends \IPS\Node\Model
if ( isset( $usedSubActions[ $key ] ) )
{
if ( \count( $usedSubActions[ $key ] ) >= \count( $subActions[ $key ] ) )
{
/* Nothing left to select, so skip this */
unset( $actions[ $key ] );
continue;
}
$disabled = $usedSubActions[ $key ];
}
@@ -499,7 +549,7 @@ class _ProfileStep extends \IPS\Node\Model
$toggles[ $key ][] = 'profile_step_subaction_' . $key;
}
}
foreach( static::canBeRequired() AS $action )
{
$toggles[ $action ][] = 'step_required';