Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Admin cannot be demoted or removed from a group when he's the only one left #509

Merged
merged 2 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 96 additions & 119 deletions includes/bp-groups/classes/class-bp-rest-group-membership-endpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ public function register_routes() {
$this->namespace,
'/' . $this->rest_base . '/(?P<group_id>[\d]+)/members',
array(
'args' => array(
'group_id' => array(
'description' => __( 'A unique numeric ID for the Group.', 'buddypress' ),
'type' => 'integer',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
Expand All @@ -84,16 +78,6 @@ public function register_routes() {
$this->namespace,
'/' . $this->rest_base . '/(?P<group_id>[\d]+)/members/(?P<user_id>[\d]+)',
array(
'args' => array(
'group_id' => array(
'description' => __( 'A unique numeric ID for the Group.', 'buddypress' ),
'type' => 'integer',
),
'user_id' => array(
'description' => __( 'A unique numeric ID for the Group Member.', 'buddypress' ),
'type' => 'integer',
),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'update_item' ),
Expand Down Expand Up @@ -320,18 +304,14 @@ public function create_item_permissions_check( $request ) {
$retval = new WP_Error(
'bp_rest_authorization_required',
__( 'Sorry, you are not allowed to perform this action.', 'buddypress' ),
array(
'status' => rest_authorization_required_code(),
)
array( 'status' => rest_authorization_required_code() )
);

if ( ! is_user_logged_in() ) {
$retval = new WP_Error(
'bp_rest_authorization_required',
__( 'Sorry, you need to be logged in to join a group.', 'buddypress' ),
array(
'status' => rest_authorization_required_code(),
)
array( 'status' => rest_authorization_required_code() )
);
} else {
$user = bp_rest_get_user( $request->get_param( 'user_id' ) );
Expand All @@ -341,9 +321,7 @@ public function create_item_permissions_check( $request ) {
$retval = new WP_Error(
'bp_rest_group_member_invalid_id',
__( 'Invalid group member ID.', 'buddypress' ),
array(
'status' => 404,
)
array( 'status' => 404 )
);
} else {
$group = $this->groups_endpoint->get_group_object( $request->get_param( 'group_id' ) );
Expand All @@ -352,19 +330,15 @@ public function create_item_permissions_check( $request ) {
$retval = new WP_Error(
'bp_rest_group_invalid_id',
__( 'Invalid group ID.', 'buddypress' ),
array(
'status' => 404,
)
array( 'status' => 404 )
);
} elseif ( bp_current_user_can( 'bp_moderate' ) || ( bp_current_user_can( 'groups_join_group', array( 'group_id' => $group->id ) ) && $loggedin_user_id === $user->ID ) ) {
$retval = true;
} else {
$retval = new WP_Error(
'bp_rest_group_member_failed_to_join',
__( 'Could not join the group.', 'buddypress' ),
array(
'status' => 500,
)
array( 'status' => 500 )
);
}
}
Expand Down Expand Up @@ -396,28 +370,35 @@ public function update_item( $request ) {
$role = $request->get_param( 'role' );
$group_id = $group->id;

$bp_loggedin_user_id = bp_loggedin_user_id();

// Check if the user is a member of the group.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns an early error for clarity when a user is not part of the group. For unban actions, we need to allow since banned users are not part of the group anymore.

if ( 'unban' !== $action && ! groups_is_user_member( $user->ID, $group_id ) ) {
return new WP_Error(
'bp_rest_group_member_not_member',
__( 'Sorry, you are not allowed to perform this action.', 'buddypress' ),
array( 'status' => 500 )
);
}

if ( 'promote' === $action ) {
if ( ! groups_promote_member( $user->ID, $group_id, $role, bp_loggedin_user_id() ) ) {
if ( ! groups_promote_member( $user->ID, $group_id, $role, $bp_loggedin_user_id ) ) {
return new WP_Error(
'bp_rest_group_member_failed_to_promote',
__( 'Could not promote member.', 'buddypress' ),
array(
'status' => 500,
)
array( 'status' => 500 )
);
}
} elseif ( 'demote' === $action && 'member' !== $role ) {
if ( ! groups_promote_member( $user->ID, $group_id, $role, bp_loggedin_user_id() ) ) {
if ( ! groups_promote_member( $user->ID, $group_id, $role, $bp_loggedin_user_id ) ) {
return new WP_Error(
'bp_rest_group_member_failed_to_demote',
__( 'Could not demote member.', 'buddypress' ),
array(
'status' => 500,
)
array( 'status' => 500 )
);
}
} elseif ( in_array( $action, array( 'demote', 'ban', 'unban' ), true ) ) {
if ( ! call_user_func( 'groups_' . $action . '_member', $user->ID, $group_id, bp_loggedin_user_id() ) ) {
if ( ! call_user_func( 'groups_' . $action . '_member', $user->ID, $group_id, $bp_loggedin_user_id ) ) {
$messages = array(
'demote' => __( 'Could not demote member from the group.', 'buddypress' ),
'ban' => __( 'Could not ban member from the group.', 'buddypress' ),
Expand All @@ -427,19 +408,14 @@ public function update_item( $request ) {
return new WP_Error(
'bp_rest_group_member_failed_to_' . $action,
$messages[ $action ],
array(
'status' => 500,
)
array( 'status' => 500 )
);
}
}

// Get updated group member.
$group_member = new BP_Groups_Member( $user->ID, $group_id );

// Setting context.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API consumer should be setting this.

$request->set_param( 'context', 'edit' );

$retval = array(
$this->prepare_response_for_collection(
$this->prepare_item_for_response( $group_member, $request )
Expand All @@ -449,7 +425,7 @@ public function update_item( $request ) {
$response = rest_ensure_response( $retval );

/**
* Fires after a group member is updated via the REST API.
* Fires after a group member status is updated via the REST API.
*
* @since 0.1.0
*
Expand All @@ -473,23 +449,23 @@ public function update_item( $request ) {
* @return true|WP_Error
*/
public function update_item_permissions_check( $request ) {
$error = new WP_Error(
$error = new WP_Error(
'bp_rest_authorization_required',
__( 'Sorry, you are not allowed to perform this action.', 'buddypress' ),
array(
'status' => rest_authorization_required_code(),
)
array( 'status' => rest_authorization_required_code() )
);

$admin_error = new WP_Error(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improving the error message.

'bp_rest_authorization_required',
__( 'Group needs 1 admin at least. Please promote a user before trying to remove this group admin.', 'buddypress' ),
array( 'status' => rest_authorization_required_code() )
);

$retval = $error;
$action = $request->get_param( 'action' );

if ( ! is_user_logged_in() ) {
$retval = new WP_Error(
'bp_rest_authorization_required',
__( 'Sorry, you need to be logged in to make an update.', 'buddypress' ),
array(
'status' => rest_authorization_required_code(),
)
);
$retval = $error;
} else {
$user = bp_rest_get_user( $request->get_param( 'user_id' ) );
$loggedin_user_id = bp_loggedin_user_id();
Expand All @@ -498,9 +474,7 @@ public function update_item_permissions_check( $request ) {
$retval = new WP_Error(
'bp_rest_group_member_invalid_id',
__( 'Invalid group member ID.', 'buddypress' ),
array(
'status' => 404,
)
array( 'status' => 404 )
);
} else {
$group = $this->groups_endpoint->get_group_object( $request->get_param( 'group_id' ) );
Expand All @@ -509,41 +483,35 @@ public function update_item_permissions_check( $request ) {
$retval = new WP_Error(
'bp_rest_group_invalid_id',
__( 'Invalid group ID.', 'buddypress' ),
array(
'status' => 404,
)
array( 'status' => 404 )
);
} elseif ( bp_current_user_can( 'bp_moderate' ) ) {
$retval = true;
} elseif ( in_array( $request->get_param( 'action' ), array( 'ban', 'unban', 'promote', 'demote' ), true ) ) {
if ( groups_is_user_admin( $loggedin_user_id, $group->id ) ) {
if ( $loggedin_user_id !== $user->ID ) {
} elseif ( bp_current_user_can( 'bp_moderate' ) && $loggedin_user_id !== $user->ID ) {
if ( 'demote' === $action && groups_is_user_admin( $user->ID, $group->id ) ) {
$group_admins = groups_get_group_admins( $group->id );

if ( 1 !== count( $group_admins ) ) {
$retval = true;
} else {
$group_admins = groups_get_group_admins( $group->id );

if ( 1 !== count( $group_admins ) ) {
$retval = true;
} else {
$retval = $error;
}
$retval = $admin_error;
}
} else {
$messages = array(
'ban' => __( 'Sorry, you are not allowed to ban this group member.', 'buddypress' ),
'unban' => __( 'Sorry, you are not allowed to unban this group member.', 'buddypress' ),
'promote' => __( 'Sorry, you are not allowed to promote this group member.', 'buddypress' ),
'demote' => __( 'Sorry, you are not allowed to demote this group member.', 'buddypress' ),
);

$retval = new WP_Error(
'bp_rest_group_member_cannot_' . $request->get_param( 'action' ),
$messages[ $request->get_param( 'action' ) ],
array(
'status' => rest_authorization_required_code(),
)
);
$retval = true;
}
} elseif ( groups_is_user_admin( $loggedin_user_id, $group->id ) && $loggedin_user_id !== $user->ID ) {
$retval = true;
} else {
$messages = array(
'ban' => __( 'Sorry, you are not allowed to ban this group member.', 'buddypress' ),
'unban' => __( 'Sorry, you are not allowed to unban this group member.', 'buddypress' ),
'promote' => __( 'Sorry, you are not allowed to promote this group member.', 'buddypress' ),
'demote' => __( 'Sorry, you are not allowed to demote this group member.', 'buddypress' ),
);

$retval = new WP_Error(
'bp_rest_group_member_cannot_' . $action,
$messages[ $action ],
array( 'status' => rest_authorization_required_code() )
);
}
}
}
Expand All @@ -568,34 +536,33 @@ public function update_item_permissions_check( $request ) {
* @return WP_REST_Response|WP_Error
*/
public function delete_item( $request ) {
// Setting context.
$request->set_param( 'context', 'edit' );
$group_id = $request->get_param( 'group_id' );
$user_id = $request->get_param( 'user_id' );

// Get the Group member before it's removed.
$member = new BP_Groups_Member( $request->get_param( 'user_id' ), $request->get_param( 'group_id' ) );
$previous = $this->prepare_item_for_response( $member, $request );
$member = new BP_Groups_Member( $user_id, $group_id );

if ( ! $member->remove() ) {
return new WP_Error(
'bp_rest_group_member_failed_to_remove',
__( 'Could not remove member from this group.', 'buddypress' ),
array(
'status' => 500,
)
array( 'status' => 500 )
);
}

$previous_member = $this->prepare_item_for_response( $member, $request );

// Build the response.
$response = new WP_REST_Response();
$response->set_data(
array(
'removed' => true,
'previous' => $previous->get_data(),
'previous' => $previous_member->get_data(),
)
);

$user = bp_rest_get_user( $request->get_param( 'user_id' ) );
$group = $this->groups_endpoint->get_group_object( $request->get_param( 'group_id' ) );
$user = bp_rest_get_user( $user_id );
$group = $this->groups_endpoint->get_group_object( $group_id );

/**
* Fires after a group member is deleted via the REST API.
Expand All @@ -622,22 +589,27 @@ public function delete_item( $request ) {
* @return true|WP_Error
*/
public function delete_item_permissions_check( $request ) {
$error = new WP_Error(
$authorization_code = rest_authorization_required_code();

$error = new WP_Error(
'bp_rest_authorization_required',
__( 'Sorry, you are not allowed to perform this action.', 'buddypress' ),
array(
'status' => rest_authorization_required_code(),
)
array( 'status' => $authorization_code )
);

$admin_error = new WP_Error(
'bp_rest_authorization_required',
__( 'Group needs 1 admin at least. Please promote a user before trying to remove this group admin.', 'buddypress' ),
array( 'status' => $authorization_code )
);

$retval = $error;

if ( ! is_user_logged_in() ) {
$retval = new WP_Error(
'bp_rest_authorization_required',
__( 'Sorry, you need to be logged in to view a group membership.', 'buddypress' ),
array(
'status' => rest_authorization_required_code(),
)
array( 'status' => $authorization_code )
);
} else {
$user = bp_rest_get_user( $request->get_param( 'user_id' ) );
Expand All @@ -647,9 +619,7 @@ public function delete_item_permissions_check( $request ) {
return new WP_Error(
'bp_rest_group_member_invalid_id',
__( 'Invalid group member ID.', 'buddypress' ),
array(
'status' => 404,
)
array( 'status' => 404 )
);
} else {
$group = $this->groups_endpoint->get_group_object( $request->get_param( 'group_id' ) );
Expand All @@ -658,21 +628,28 @@ public function delete_item_permissions_check( $request ) {
$retval = new WP_Error(
'bp_rest_group_invalid_id',
__( 'Invalid group ID.', 'buddypress' ),
array(
'status' => 404,
)
array( 'status' => 404 )
);
} elseif ( bp_current_user_can( 'bp_moderate' ) || ( $user->ID !== $loggedin_user_id && groups_is_user_admin( $loggedin_user_id, $group->id ) ) ) {
$retval = true;
} elseif ( bp_current_user_can( 'bp_moderate' ) || ( groups_is_user_admin( $loggedin_user_id, $group->id ) && $loggedin_user_id !== $user->ID ) ) {
if ( groups_is_user_admin( $user->ID, $group->id ) ) {
$group_admins = groups_get_group_admins( $group->id );

if ( 1 !== count( $group_admins ) ) {
$retval = true;
} else {
$retval = $admin_error;
}
} else {
$retval = true;
}
} elseif ( $user->ID === $loggedin_user_id && ! groups_is_user_banned( $user->ID, $group->id ) ) {
$group_admins = groups_get_group_admins( $group->id );
if ( groups_is_user_admin( $loggedin_user_id, $group->id ) ) {
$group_admins = groups_get_group_admins( $group->id );

// Special case for self-removal: don't allow if it'd leave a group with no admins.
if ( in_array( $loggedin_user_id, wp_list_pluck( $group_admins, 'user_id' ), true ) ) {
if ( 1 !== count( $group_admins ) ) {
$retval = true;
} else {
$retval = $error;
$retval = $admin_error;
}
} else {
$retval = true;
Expand Down
Loading