Version 4.6.0

This commit is contained in:
Neo committed 2025-12-19 05:55:31 -08:00
1 parent f79dcf067a
commit 517a5e1f70
2036 files changed
+110041 -26162

No files matched your search

+197 -33
View File
@@ -22,6 +22,14 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _members extends \IPS\Api\Controller
{
/**
* @brief Parameters to mask in logs. Keys are the method names and values an array of field or request keys.
*/
public $parametersToMask = array(
'POSTindex' => array( 'password' ),
'POSTitem' => array( 'password' ),
);
/**
* GET /core/members
* Get list of members
@@ -134,8 +142,11 @@ class _members extends \IPS\Api\Controller
{
$existingUsername = \IPS\Member::load( \IPS\Request::i()->name, 'name' );
if ( !$existingUsername->member_id )
{
$member->logHistory( 'core', 'display_name', array( 'old' => $member->name, 'new' => \IPS\Request::i()->name, 'by' => 'api' ) );
{
if ( $member->member_id )
{
$member->logHistory( 'core', 'display_name', array( 'old' => $member->name, 'new' => \IPS\Request::i()->name, 'by' => 'api' ) );
}
$member->name = \IPS\Request::i()->name;
}
else
@@ -148,10 +159,15 @@ class _members extends \IPS\Api\Controller
{
$existingEmail = \IPS\Member::load( \IPS\Request::i()->email, 'email' );
if ( !$existingEmail->member_id )
{
$member->logHistory( 'core', 'email_change', array( 'old' => $member->name, 'new' => \IPS\Request::i()->name, 'by' => 'api' ) );
{
/* Only do this if we are updating a member */
if ( $member->member_id )
{
$member->logHistory( 'core', 'email_change', array( 'old' => $member->email, 'new' => \IPS\Request::i()->email, 'by' => 'api' ) );
$member->invalidateSessionsAndLogins();
}
$member->email = \IPS\Request::i()->email;
$member->invalidateSessionsAndLogins();
}
else
{
@@ -161,15 +177,50 @@ class _members extends \IPS\Api\Controller
if ( isset( \IPS\Request::i()->group ) )
{
try
if( \is_array( \IPS\Request::i()->group ) )
{
$group = \IPS\Member\Group::load( \IPS\Request::i()->group );
$member->member_group_id = $group->g_id;
$groups = \IPS\Request::i()->group;
$group = array_shift( $groups );
try
{
$group = \IPS\Member\Group::load( $group );
$member->member_group_id = $group->g_id;
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_GROUP', '1C292/6', 403 );
}
if( \count( $groups ) )
{
if( !isset( \IPS\Request::i()->secondaryGroups ) )
{
\IPS\Request::i()->secondaryGroups = $groups;
}
else
{
if( !\is_array( \IPS\Request::i()->secondaryGroups ) )
{
\IPS\Request::i()->secondaryGroups = array( \IPS\Request::i()->secondaryGroups );
}
\IPS\Request::i()->secondaryGroups = array_merge( \IPS\Request::i()->secondaryGroups , \IPS\Request::i()->group );
}
}
}
catch ( \OutOfRangeException $e )
else
{
throw new \IPS\Api\Exception( 'INVALID_GROUP', '1C292/6', 403 );
try
{
$group = \IPS\Member\Group::load( \IPS\Request::i()->group );
$member->member_group_id = $group->g_id;
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_GROUP', '1C292/6', 403 );
}
}
}
if( isset( \IPS\Request::i()->secondaryGroups ) AND \is_array( \IPS\Request::i()->secondaryGroups ) )
@@ -202,7 +253,7 @@ class _members extends \IPS\Api\Controller
{
foreach( \IPS\Request::i()->rawProperties as $property => $value )
{
$member->$property = $value;
$member->$property = \is_numeric( $value ) ? (int) $value : $value;
}
}
@@ -288,7 +339,7 @@ class _members extends \IPS\Api\Controller
* @apiparam string name Username
* @apiparam string email Email address
* @apiparam string password Password (standard login handler only). If not provided, the member will be emailed to set one.
* @apiparam int group Group ID number
* @apiparam int|array group Group ID number ; if an array was provided, the first item will be used for the primary group and everything else for the secondary.
* @apiparam string registrationIpAddress IP Address
* @apiparam array secondaryGroups Secondary group IDs, or empty value to reset secondary groups
* @apiparam object customFields Array of custom fields as fieldId => fieldValue
@@ -339,7 +390,7 @@ class _members extends \IPS\Api\Controller
* @apiparam string name Username
* @apiparam string email Email address
* @apiparam string password Password (standard login handler only)
* @apiparam int group Group ID number
* @apiparam int|array group Group ID number ; if an array was provided, the first item will be used for the primary group and everything else for the secondary.
* @apiparam string registrationIpAddress IP Address
* @apiparam array secondaryGroups Secondary group IDs, or empty value to reset secondary groups
* @apiparam object customFields Array of custom fields as fieldId => fieldValue
@@ -391,7 +442,7 @@ class _members extends \IPS\Api\Controller
*
* @apiclientonly
* @apiparam string contentAction delete|hide|leave
* @apiparam bool contentAnonymize Keep member name or anonymize content
* @apiparam bool contentAnonymize Keep member name = 1, anonymize content = 0
* @param int $id ID Number
* @throws 1C292/2 INVALID_ID The member ID does not exist
* @return void
@@ -415,6 +466,9 @@ class _members extends \IPS\Api\Controller
$member->delete( FALSE );
break;
case 'hide':
$member->hideOrDeleteAllContent( 'hide' );
$member->delete( TRUE, (bool) \IPS\Request::i()->contentAnonymize ?: FALSE );
break;
case 'leave':
$member->delete( TRUE, (bool) \IPS\Request::i()->contentAnonymize ?: FALSE );
break;
@@ -516,12 +570,12 @@ class _members extends \IPS\Api\Controller
{
$classToFollow = 'IPS\\' . \IPS\Request::i()->followApp . '\\' . mb_ucfirst( \IPS\Request::i()->followArea );
if( !class_exists( $classToFollow ) )
if( !class_exists( $classToFollow ) or !array_key_exists( \IPS\Request::i()->follow_app, \IPS\Application::applications() ) )
{
throw new \OutOfRangeException;
}
$thingToFollow = $classToFollow::load( \IPS\Request::i()->followId );
$thingToFollow = $classToFollow::load( (int) \IPS\Request::i()->followId );
}
catch( \Exception $e )
{
@@ -546,9 +600,34 @@ class _members extends \IPS\Api\Controller
$follow->notify_do = ( isset( \IPS\Request::i()->followType ) AND \IPS\Request::i()->followType == 'none' ) ? 0 : ( ( isset( \IPS\Request::i()->followNotify ) ) ? (int) \IPS\Request::i()->followNotify : 1 );
$follow->notify_freq = ( isset( \IPS\Request::i()->followType ) AND \in_array( \IPS\Request::i()->followType, array( 'none', 'immediate', 'daily', 'weekly' ) ) ) ? \IPS\Request::i()->followType : 'immediate';
$follow->save();
/* Delete cache */
\IPS\Db::i()->delete( 'core_follow_count_cache', array( 'class=? AND id=?', $classToFollow, $follow->rel_id ) );
/* If we're following a member, add points */
if ( $follow->app == 'core' and $follow->area == 'member' )
{
/* Give points */
$receiver = \IPS\Member::load( $follow->rel_id );
$receiver->achievementAction( 'core', 'FollowMember', [
'giver' => \IPS\Member::load( $follow->member_id )
] );
}
else if ( \in_array( 'IPS\Node\Model', class_parents( $classToFollow ) ) )
{
$member->achievementAction( 'core', 'FollowNode', $classToFollow::load( \IPS\Request::i()->followId ) );
}
else if ( \in_array( 'IPS\Content\Item', class_parents( $classToFollow ) ) )
{
$item = $classToFollow::load( \IPS\Request::i()->followId );
$member->achievementAction( 'core', 'FollowContentItem', [
'item' => $item,
'author' => $item->author()
] );
}
/* If we're following a club, follow all nodes in the club automatically */
if( $follow->app == 'core' and $follow->area == 'club' )
if ( $follow->app == 'core' and $follow->area == 'club' )
{
$thing = \IPS\Member\Club::loadAndCheckPerms( $follow->rel_id );
@@ -576,6 +655,9 @@ class _members extends \IPS\Api\Controller
$nodeFollow->notify_do = $follow->notify_do;
$nodeFollow->notify_freq = $follow->notify_freq;
$nodeFollow->save();
/* Delete cache */
\IPS\Db::i()->delete( 'core_follow_count_cache', array( 'class=? AND id=?', $itemClass, $nodeFollow->rel_id ) );
}
}
@@ -698,10 +780,10 @@ class _members extends \IPS\Api\Controller
/* Return */
return new \IPS\Api\PaginatedResponse(
200,
\IPS\Db::i()->select( '*', 'core_notifications', array( '`member`=?', $member->member_id ), "updated_time DESC" ),
\IPS\Db::i()->select( '*', 'core_notifications', array( "`member`=? AND notification_app IN('" . implode( "','", array_keys( \IPS\Application::enabledApplications() ) ) . "')", $member->member_id ), "updated_time DESC" ),
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
'IPS\Notification\Inline',
\IPS\Db::i()->select( 'COUNT(*)', 'core_notifications', array( '`member`=?', $member->member_id ) )->first(),
\IPS\Db::i()->select( 'COUNT(*)', 'core_notifications', array( "`member`=? AND notification_app IN('" . implode( "','", array_keys( \IPS\Application::enabledApplications() ) ) . "')", $member->member_id ) )->first(),
$this->member,
isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : NULL
);
@@ -824,6 +906,10 @@ class _members extends \IPS\Api\Controller
* @throws 2C292/G NO_PERMISSION The authorized user does not have permission to warn the member
* @throws 2C292/O INVALID_ID The member could not be found
* @throws 1C292/Y MODERATOR_REQUIRED When not using an OAuth access token a moderator member ID must be supplied
* @throws 1C292/Z INVALID_DATE An invalid datetime was supplied for the expire parameter
* @throws 1C292/12 INVALID_DATE An invalid datetime was supplied for the suspend parameter
* @throws 1C292/11 INVALID_DATE An invalid datetime was supplied for the restrictPosts parameter
* @throws 1C292/10 INVALID_DATE An invalid datetime was supplied for the modQueue parameter
* @note The warning will be issued as the current authorized user if using an OAuth access token, otherwise the 'moderator' parameter must be specified to indicate which moderator the warning should be issued from
*/
public function POSTitem_warnings( $id )
@@ -878,7 +964,14 @@ class _members extends \IPS\Api\Controller
}
else
{
$options['warn_remove'] = new \IPS\DateTime( \IPS\Request::i()->expire );
try
{
$options['warn_remove'] = new \IPS\DateTime( \IPS\Request::i()->expire );
}
catch( \Exception $e )
{
throw new \IPS\Api\Exception( 'INVALID_DATE', '1C292/Z', 404 );
}
}
}
@@ -886,11 +979,20 @@ class _members extends \IPS\Api\Controller
{
if( \IPS\Request::i()->modQueue == -1 )
{
$options['warn_punishment']['mq'] = \IPS\Request::i()->modQueue;
$options['warn_punishment'][] = 'mq';
$options['warn_mq'] = \IPS\Request::i()->modQueue;
}
else
{
$options['warn_punishment']['mq'] = new \IPS\DateTime( \IPS\Request::i()->modQueue );
try
{
$options['warn_punishment'][] = 'mq';
$options['warn_mq'] = new \IPS\DateTime( \IPS\Request::i()->modQueue );
}
catch( \Exception $e )
{
throw new \IPS\Api\Exception( 'INVALID_DATE', '1C292/10', 404 );
}
}
}
@@ -898,11 +1000,20 @@ class _members extends \IPS\Api\Controller
{
if( \IPS\Request::i()->restrictPosts == -1 )
{
$options['warn_punishment']['rpa'] = \IPS\Request::i()->restrictPosts;
$options['warn_punishment'][] = 'rpa';
$options['warn_rpa'] = \IPS\Request::i()->restrictPosts;
}
else
{
$options['warn_punishment']['rpa'] = new \IPS\DateTime( \IPS\Request::i()->restrictPosts );
try
{
$options['warn_punishment'][] = 'rpa';
$options['warn_rpa'] = new \IPS\DateTime( \IPS\Request::i()->restrictPosts );
}
catch( \Exception $e )
{
throw new \IPS\Api\Exception( 'INVALID_DATE', '1C292/11', 404 );
}
}
}
@@ -910,11 +1021,20 @@ class _members extends \IPS\Api\Controller
{
if( \IPS\Request::i()->suspend == -1 )
{
$options['warn_punishment']['suspend'] = \IPS\Request::i()->suspend;
$options['warn_punishment'][] = 'suspend';
$options['warn_suspend'] = \IPS\Request::i()->suspend;
}
else
{
$options['warn_punishment']['suspend'] = new \IPS\DateTime( \IPS\Request::i()->suspend );
try
{
$options['warn_punishment'][] = 'suspend';
$options['warn_suspend'] = new \IPS\DateTime( \IPS\Request::i()->suspend );
}
catch( \Exception $e )
{
throw new \IPS\Api\Exception( 'INVALID_DATE', '1C292/12', 404 );
}
}
}
@@ -971,7 +1091,7 @@ class _members extends \IPS\Api\Controller
*
* @param int $id ID Number
* @param int $warningId Warning ID
* @apiparam bool undoOnly Only undo the warning but do not delete it
* @apiparam bool deleteOnly Delete the warning but do not revoke the consequences
* @throws 2C292/P INVALID_ID The member could not be found
* @throws 2C292/Q INVALID_WARNING The warning could not be loaded or the current authorized user does not have permission to delete the warning
* @return void
@@ -1003,13 +1123,13 @@ class _members extends \IPS\Api\Controller
}
/* Revoke the warning */
$warning->undo();
if( !isset( \IPS\Request::i()->undoOnly ) OR !\IPS\Request::i()->undoOnly )
if( !isset( \IPS\Request::i()->deleteOnly ) OR !\IPS\Request::i()->deleteOnly )
{
$warning->delete();
$warning->undo();
}
$warning->delete();
return new \IPS\Api\Response( 200, NULL );
}
catch ( \OutOfRangeException $e )
@@ -1070,4 +1190,48 @@ class _members extends \IPS\Api\Controller
throw new \IPS\Api\Exception( 'INVALID_ID', '2C292/N', 404 );
}
}
}
/**
* POST /core/members/{id}/achievements/{badge}/awardbadge
* Acknowledge a warning
*
* @param int $id ID Number
* @reqapiparam int $badgeId Badge to award
* @param int $logId The ID number of the log in core_achievements_log which will tell us specifically what ACTION caused this (eg what post was made)
* @param int $ruleId The ID number of the rule which matched that action which caused this badge to be awarded
* @param array $actor If member is 'subject', 'other' or both
* @param int $recognize The ID of the recognize table row (optional)
* @return \IPS\Member
* @throws 2C292/M INVALID_BADGE The badge could not be found //TODo error code
* @throws 2C292/N INVALID_ID The member could not be found //TODo error code
*/
public function POSTitem_achievements_awardbadge( int $id, int $badgeId, int $logId, int $ruleId, array $actor, int $recognize=0 ): \IPS\Api\Response
{
try
{
/* Load member */
$member = \IPS\Member::load( $id );
if( !$member->member_id )
{
throw new \OutOfRangeException;
}
/* Load the badge */
try
{
$badge = \IPS\core\Achievements\Badge::load( $badgeId );
}
catch( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_BADGE', '2C292/W', 404 ); //TODO error code
}
$member->awardBadge($id, $badgeId, $logId,$ruleId,$actor,$recognize);
return new \IPS\Api\Response( 200, $member->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '2C292/N', 404 ); //TODO error code
}
}
}