Version 5.0.13
This commit is contained in:
1 parent
b89858b2ff
commit
5efe744c5f
1407 files changed
+69690
-30177
No files matched your search
+152
-28
@@ -34,7 +34,6 @@ use IPS\core\ProfileFields\Api\Field;
|
||||
use IPS\core\ProfileFields\Api\FieldGroup;
|
||||
use IPS\core\Reports\Report;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Db;
|
||||
use IPS\Db\Select;
|
||||
use IPS\Events\Event;
|
||||
use IPS\Helpers\CoverPhoto;
|
||||
@@ -57,7 +56,6 @@ use IPS\Member\UserMenu;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use IPS\Patterns\ActiveRecordIterator;
|
||||
use IPS\Platform\Bridge;
|
||||
use IPS\Settings;
|
||||
use IPS\Text\Encrypt;
|
||||
use IPS\Xml\SimpleXML;
|
||||
use OutOfRangeException;
|
||||
@@ -92,6 +90,7 @@ class Member extends ActiveRecord
|
||||
{
|
||||
Followable::followersCount as public _followersCountBase;
|
||||
Followable::followers as public _followersBase;
|
||||
Followable::follow as public _followBase;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +206,7 @@ class Member extends 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
|
||||
'email_messages_bounce' => 134217728, // @deprecated User's email keeps returning hard bounces so they need to change it
|
||||
// 268435456 - cookie_optout moved to cookie preferences
|
||||
'expert_user_disabled' => 536870912, // User does not want to be a community expert
|
||||
'expert_user_blocked' => 1073741824 // Admin has blocked expert user
|
||||
@@ -542,12 +541,6 @@ class Member extends ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
@@ -564,8 +557,8 @@ class Member extends ActiveRecord
|
||||
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'] ) ) ) )
|
||||
/* Run member sync, but not if the only change is the last_activity timestamp and if we're not just updating the member's language during language() init when no language is set */
|
||||
if( ( Member::loggedIn()->member_id != $this->member_id or $this->_lang !== null ) and ( count( $this->changedCustomFields ) > 0 OR ( count( $changes ) > 0 AND !( count( $changes ) === 1 AND isset( $changes['last_activity'] ) ) ) ) )
|
||||
{
|
||||
Event::fire( 'onProfileUpdate', $this, array( 'changes' => array_merge( $changes, $this->changedCustomFields ) ) );
|
||||
}
|
||||
@@ -781,7 +774,7 @@ class Member extends ActiveRecord
|
||||
/* Run sync */
|
||||
$this->logHistory( 'core', 'account', array( 'type' => 'spammer', 'set' => TRUE, 'actions' => $actions ) );
|
||||
Event::fire( 'onSetAsSpammer', $this );
|
||||
|
||||
Webhook::fire( 'member_flagged_as_spammer', $this );
|
||||
/* Notify admin */
|
||||
AdminNotification::send( 'core', 'Spammer', NULL, TRUE, $this->member_id, Member::loggedIn() );
|
||||
|
||||
@@ -865,6 +858,7 @@ class Member extends ActiveRecord
|
||||
|
||||
/* Run sync */
|
||||
Event::fire( 'onUnSetAsSpammer', $this );
|
||||
Webhook::fire( 'member_unflagged_as_spammer', $this );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -908,6 +902,8 @@ class Member extends ActiveRecord
|
||||
{
|
||||
Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'member_id=?', $this->member_id ) );
|
||||
}
|
||||
|
||||
Webhook::fire( 'member_ban_state_changed', [$this, $value] );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1692,6 +1688,31 @@ class Member extends ActiveRecord
|
||||
return static::_followers( 'member', $this->member_id ?: 0, $privacy, $frequencyTypes, $date, $limit, $order, FALSE, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow this object
|
||||
*
|
||||
* @param string $frequency ( 'none', 'immediate', 'daily', 'weekly' )
|
||||
* @param bool $public
|
||||
* @param Member|null $member
|
||||
* @return void
|
||||
*/
|
||||
public function follow( string $frequency, bool $public=true, ?Member $member=null ) : void
|
||||
{
|
||||
$member = $member ?: Member::loggedIn();
|
||||
$this->_followBase( $frequency, $public, $member );
|
||||
|
||||
if( $public )
|
||||
{
|
||||
$this->achievementAction( 'core', 'FollowMember', [
|
||||
'giver' => $member
|
||||
] );
|
||||
|
||||
$notification = new Notification( Application::load( 'core' ), 'member_follow', $member, array( $member ) );
|
||||
$notification->recipients->attach( $this );
|
||||
$notification->send();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a failed login attempt
|
||||
*
|
||||
@@ -1823,7 +1844,7 @@ class Member extends ActiveRecord
|
||||
/**
|
||||
* Get timezone
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_timezone(): ?string
|
||||
{
|
||||
@@ -2043,6 +2064,12 @@ class Member extends ActiveRecord
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Is the member in a group that is allowed to be an expert? This setting might have been changed. */
|
||||
if( Settings::i()->ips_experts_allowed_groups !== '*' and ! $this->inGroup( explode( ',', Settings::i()->ips_experts_allowed_groups ) ) )
|
||||
{
|
||||
$this->isExpert = false;
|
||||
}
|
||||
|
||||
if ( $this->isExpert === null )
|
||||
{
|
||||
try
|
||||
@@ -2343,7 +2370,7 @@ class Member extends ActiveRecord
|
||||
{
|
||||
if( $this->_url === NULL )
|
||||
{
|
||||
$this->_url = Url::internal( "app=core&module=members&controller=profile&id={$this->member_id}", 'front', 'profile', $this->members_seo_name );
|
||||
$this->_url = Url::internal( "app=core&module=members&controller=profile&id={$this->member_id}", 'front', 'profile', ( $this->members_seo_name ?? '-' ) );
|
||||
}
|
||||
|
||||
return $this->_url;
|
||||
@@ -3172,6 +3199,21 @@ class Member extends ActiveRecord
|
||||
return $this->_following[ $_key ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear the cached result used by the following method
|
||||
*
|
||||
* @param string $app Application key
|
||||
* @param string $area Area
|
||||
* @param int $id Item ID
|
||||
* @return void
|
||||
*/
|
||||
public function clearFollowingCache( string $app, string $area, int $id ): void
|
||||
{
|
||||
$key = md5( $app . $area . $id );
|
||||
unset( $this->_following[ $key ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin CP Restrictions
|
||||
*
|
||||
@@ -3778,12 +3820,18 @@ class Member extends ActiveRecord
|
||||
* @clientapiresponse datetime|null lastVisit Last distinct visit date on the site.
|
||||
* @clientapiresponse datetime|null lastPost Latest content submission date.
|
||||
* @apiresponse int profileViews Number of times member's profile has been viewed
|
||||
* @clientapiresponse bool spammer Is the member flagged as a spammer?
|
||||
* @clientapiresponse bool banned Is the member currently banned?
|
||||
* @apiresponse string birthday Member birthday in MM/DD/YYYY format (or MM/DD format if no year has been supplied).
|
||||
* @apiresponse array login_methods Array of login methods used <int, array{name: string, id: int|string}>, key is login method ID.
|
||||
* @apiresponse [\IPS\core\ProfileFields\Api\FieldGroup] customFields Custom profile fields. For requests using an OAuth Access Token for a particular member, only fields the authorized user can view will be included
|
||||
* @apiresponse [\IPS\core\Achievements\Rank] rank Rank
|
||||
* @apiresponse int achievements_points Points
|
||||
* @apiresponse bool allowAdminEmails Whether or not this member wants to receive admin emails
|
||||
* @clientapiresponse bool allowAdminEmails Whether or not this member wants to receive admin emails
|
||||
* @apiresponse bool completed Whether or not the registration is completed
|
||||
* @clientapiresponse int totalMessages Total number of Personal Messages
|
||||
* @clientapiresponse int unreadMessages Total number of unread Personal Messages
|
||||
* @apiresponse [IPS\core\Achievements\Badge] badges All badges received by this member
|
||||
*/
|
||||
public function apiOutput( Member $authorizedMember = NULL, array $otherFields = NULL ): array
|
||||
{
|
||||
@@ -3837,7 +3885,7 @@ class Member extends ActiveRecord
|
||||
$fields[ $profileFieldGroup ] = ( new FieldGroup( $this->language()->get( 'core_pfieldgroups_' . $profileFieldGroup ), $groupValues ) )->apiOutput( $authorizedMember );
|
||||
}
|
||||
}
|
||||
catch( UnderflowException $e ) { } # Guests will not have any profile field information.
|
||||
catch( UnderflowException $e ) { } # Guests will not have any profile field information
|
||||
|
||||
$return = array();
|
||||
$return['id'] = $this->member_id;
|
||||
@@ -3881,8 +3929,29 @@ class Member extends ActiveRecord
|
||||
if( !$authorizedMember OR $authorizedMember->member_id === $this->member_id )
|
||||
{
|
||||
$return['birthday'] = $this->bday_month ? ( $this->bday_month . '/' . $this->bday_day . ( $this->bday_year ? '/' . $this->bday_year : '' ) ) : NULL;
|
||||
|
||||
/* Login Links */
|
||||
$return['login_methods'] = [];
|
||||
foreach( Db::i()->select( '*', 'core_login_links', [ 'token_member=? and token_linked=?', $this->member_id, 1 ] ) as $link )
|
||||
{
|
||||
/* Only include enabled methods */
|
||||
if( isset( Login::methods()[ $link['token_login_method'] ] ) )
|
||||
{
|
||||
$return['login_methods'][ $link['token_login_method'] ] = [
|
||||
'name' => Login::methods()[ $link['token_login_method'] ]->_title,
|
||||
'id' => $link['token_identifier']
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
$return['profileViews'] = $this->members_profile_views;
|
||||
|
||||
if( $authorizedMember === null or $authorizedMember->member_id == $this->member_id )
|
||||
{
|
||||
$return['spammer'] = (bool) $this->members_bitoptions['bw_is_spammer'];
|
||||
$return['banned'] = (bool) $this->isBanned();
|
||||
}
|
||||
|
||||
$return['customFields'] = $fields;
|
||||
|
||||
if ( !$authorizedMember )
|
||||
@@ -3898,9 +3967,29 @@ class Member extends ActiveRecord
|
||||
|
||||
$return['rank'] = $this->rank() ? $this->rank()->apiOutput( $authorizedMember ) : NULL;
|
||||
$return['achievements_points'] = $this->achievements_points;
|
||||
$return['allowAdminEmails'] = $this->allow_admin_mails;
|
||||
|
||||
if( $authorizedMember === null or $authorizedMember->member_id == $this->member_id )
|
||||
{
|
||||
$return['allowAdminEmails'] = $this->allow_admin_mails;
|
||||
}
|
||||
|
||||
$return['completed'] = $this->completed;
|
||||
|
||||
$counts = \IPS\Db::i()->select( 'COUNT(map_id) as total_count, COUNT(CASE WHEN map_has_unread = 1 THEN 1 END) as unread_messages', 'core_message_topic_user_map', [ 'map_user_id=?', $this->member_id ] )->first();
|
||||
|
||||
if( $authorizedMember === null or $authorizedMember->member_id == $this->member_id )
|
||||
{
|
||||
$return['totalMessages'] = $counts['total_count'];
|
||||
$return['unreadMessages'] = $counts['unread_messages'];
|
||||
}
|
||||
|
||||
/* Add member badges */
|
||||
$return['badges'] = array();
|
||||
foreach( $this->recentBadges( NULL ) as $badge )
|
||||
{
|
||||
$return['badges'][] = $badge->apiOutput();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -4695,7 +4784,7 @@ class Member extends ActiveRecord
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
/* Log the error as part of the failure audit trail */
|
||||
\IPS\Log::log( "Group promotion found a member (#{$this->member_id}: {$this->name} [Group ID {$this->member_group_id}] with an invalid member group", 'group_promotion' );
|
||||
Log::log( "Group promotion found a member (#{$this->member_id}: {$this->name} [Group ID {$this->member_group_id}] with an invalid member group", 'group_promotion' );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5223,11 +5312,27 @@ class Member extends ActiveRecord
|
||||
*/
|
||||
protected function _sendWelcomeEmail() : void
|
||||
{
|
||||
try
|
||||
if( Settings::i()->reg_welcome_email )
|
||||
{
|
||||
Email::buildFromTemplate( 'core', 'registration_complete', array( $this ), Email::TYPE_TRANSACTIONAL )->send( $this );
|
||||
if( $this->language()->checkKeyExists( 'reg_welcome_email_message' ) )
|
||||
{
|
||||
$message = $this->language()->get( 'reg_welcome_email_message' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$message = $this->language()->addToStack( 'email_reg_complete', false, [ 'sprintf' => [ Settings::i()->board_name ] ] );
|
||||
if( $this->members_pass_hash )
|
||||
{
|
||||
$message .= $this->language()->addToStack( 'email_reg_complete_pass' );
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Email::buildFromTemplate( 'core', 'registration_complete', array( $this, $message ), Email::TYPE_TRANSACTIONAL )->send( $this );
|
||||
}
|
||||
catch( ErrorException $e ) { }
|
||||
}
|
||||
catch( ErrorException $e ) { }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5264,7 +5369,7 @@ class Member extends ActiveRecord
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Profile Sync
|
||||
*
|
||||
@@ -5274,7 +5379,7 @@ class Member extends ActiveRecord
|
||||
{
|
||||
$profileSync = $this->profilesync;
|
||||
|
||||
foreach (Login::methods() as $method )
|
||||
foreach ( Login::methods() as $method )
|
||||
{
|
||||
if ( $method->canProcess( $this ) )
|
||||
{
|
||||
@@ -5285,6 +5390,16 @@ class Member extends ActiveRecord
|
||||
$profileSync[ $type ] = array( 'handler' => $method->_id, 'ref' => NULL, 'error' => NULL );
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform any extra profile sync the login handler may need */
|
||||
try
|
||||
{
|
||||
if( method_exists( $method, 'extraProfileSync' ) )
|
||||
{
|
||||
$method->extraProfileSync( $this );
|
||||
}
|
||||
}
|
||||
catch( Login\Exception $e ) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5571,7 +5686,7 @@ class Member extends ActiveRecord
|
||||
if ( $this->member_id )
|
||||
{
|
||||
/* Set this only if this was not called by the task system, otherwise we'll set the member who triggered the task as moderator */
|
||||
if ( Dispatcher::hasInstance() and !Dispatcher::i()->inDestructor and $by === NULL )
|
||||
if ( Dispatcher::hasInstance() and isset( Dispatcher::i()->inDestructor ) and !Dispatcher::i()->inDestructor and $by === NULL )
|
||||
{
|
||||
$by = Session::i()->member; // Not \IPS\Member::loggedIn() because if this is an admin logged in as a member, we want to log that the action was done by the admin
|
||||
}
|
||||
@@ -6300,6 +6415,12 @@ class Member extends ActiveRecord
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Cloudy stuff */
|
||||
if( !Bridge::i()->checkAchievementRule( array_merge( [ 'action' => "{$app}_{$extension}" ], $ruleData ), $this ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !$ruleData['filters'] or $extensionObject->filtersMatch( $this, $ruleData['filters'], $extra ) )
|
||||
{
|
||||
/* Did we already award for this milestone? */
|
||||
@@ -6364,6 +6485,9 @@ class Member extends ActiveRecord
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fire the post-achievement here */
|
||||
Bridge::i()->postAchievement( $ruleId, array_merge( [ 'action' => "{$app}_{$extension}" ], $ruleData ), $this, $extra );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6631,7 +6755,7 @@ class Member extends ActiveRecord
|
||||
{
|
||||
$lastLogin = Db::i()->select( 'member_timestamp', 'core_members_logins', [ 'member_id=?', $this->member_id ], 'member_timestamp DESC', 1 )->first();
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
catch( UnderflowException $e )
|
||||
{
|
||||
/* This is the first login, or they haven't logged in since upgrading from < 106100 */
|
||||
try
|
||||
@@ -6642,7 +6766,7 @@ class Member extends ActiveRecord
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
catch( UnderflowException $e )
|
||||
{
|
||||
/* This could be a new install, or IN_DEV where upgrade history isn't available */
|
||||
return false;
|
||||
@@ -6658,7 +6782,7 @@ class Member extends ActiveRecord
|
||||
}
|
||||
|
||||
/* If this login is within the period, don't send an email */
|
||||
if( $lastLoginObj > DateTime::create()->sub( new \DateInterval( static::LOGIN_INACTIVITY_NOTIFICATION ) ) )
|
||||
if( $lastLoginObj > DateTime::create()->sub( new DateInterval( static::LOGIN_INACTIVITY_NOTIFICATION ) ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -6669,7 +6793,7 @@ class Member extends ActiveRecord
|
||||
{
|
||||
$location = GeoLocation::getRequesterLocation();
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
catch ( Exception $e )
|
||||
{
|
||||
$location = NULL;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user