array( 'type' => 'integer', 'required' => true, ), ); register_rest_route( $this->namespace, '/spaces', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'permission_community' ), 'args' => array( 'scope' => array( 'type' => 'string', 'enum' => array( 'all', 'joined' ), 'default' => 'all', ), ), ), array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'create_item' ), 'permission_callback' => array( $this, 'permission_manage' ), ), ) ); register_rest_route( $this->namespace, '/spaces/reorder', array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'reorder' ), 'permission_callback' => array( $this, 'permission_manage' ), 'args' => array( 'ids' => array( 'type' => 'array', 'items' => array( 'type' => 'integer' ), 'required' => true, ), ), ), ) ); register_rest_route( $this->namespace, '/spaces/slug/(?P[a-z0-9-]+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item_by_slug' ), 'permission_callback' => array( $this, 'permission_community' ), ), ) ); register_rest_route( $this->namespace, '/spaces/(?P\d+)', array( 'args' => $id_arg, array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'permission_community' ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'permission_logged_in' ), ), array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete_item' ), 'permission_callback' => array( $this, 'permission_manage' ), ), ) ); register_rest_route( $this->namespace, '/spaces/(?P\d+)/join', array( 'args' => $id_arg, array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'join' ), 'permission_callback' => array( $this, 'permission_logged_in' ), ), ) ); register_rest_route( $this->namespace, '/spaces/(?P\d+)/leave', array( 'args' => $id_arg, array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'leave' ), 'permission_callback' => array( $this, 'permission_logged_in' ), ), ) ); register_rest_route( $this->namespace, '/space-groups', array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'create_group' ), 'permission_callback' => array( $this, 'permission_manage' ), ), ) ); register_rest_route( $this->namespace, '/space-groups/reorder', array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'reorder' ), 'permission_callback' => array( $this, 'permission_manage' ), 'args' => array( 'ids' => array( 'type' => 'array', 'items' => array( 'type' => 'integer' ), 'required' => true, ), ), ), ) ); register_rest_route( $this->namespace, '/space-groups/(?P\d+)', array( 'args' => $id_arg, array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_group' ), 'permission_callback' => array( $this, 'permission_manage' ), ), array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete_group' ), 'permission_callback' => array( $this, 'permission_manage' ), ), ) ); } /** * Lists the spaces and groups the viewer can see. * * @param WP_REST_Request $request Request. * @return WP_REST_Response */ public function get_items( $request ) { $user_id = get_current_user_id(); $joined = 'joined' === $request->get_param( 'scope' ); $spaces = array(); foreach ( SpaceRepository::all( 'space' ) as $space ) { if ( ! SpaceAccess::can_see( $space, $user_id ) ) { continue; } if ( $joined ) { $membership = Membership::get( $space['id'], $user_id ); if ( ! $membership || 'active' !== $membership['status'] ) { continue; } } $spaces[] = SpaceFormatter::space( $space, $user_id ); } return rest_ensure_response( array( 'groups' => array_map( array( SpaceFormatter::class, 'group' ), SpaceRepository::all( 'group' ) ), 'spaces' => $spaces, ) ); } /** * Returns one space by ID. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function get_item( $request ) { $space = $this->visible_space( $request['id'] ); if ( is_wp_error( $space ) ) { return $space; } return rest_ensure_response( SpaceFormatter::space( $space, get_current_user_id() ) ); } /** * Returns one space by slug. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function get_item_by_slug( $request ) { $found = SpaceRepository::find_by_slug( $request['slug'] ); $space = $this->visible_space( $found ? $found['id'] : 0 ); if ( is_wp_error( $space ) ) { return $space; } return rest_ensure_response( SpaceFormatter::space( $space, get_current_user_id() ) ); } /** * Creates a space. The creator becomes its admin. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function create_item( $request ) { $data = SpaceRepository::clean( $this->space_input( $request ), true ); if ( is_wp_error( $data ) ) { return $data; } $data['type'] = 'space'; $user_id = get_current_user_id(); $id = SpaceRepository::create( $data, $user_id ); if ( ! $id ) { return new WP_Error( 'spacebay_space_create', __( 'The space could not be created. Please try again.', 'spacebay' ), array( 'status' => 500 ) ); } Membership::set( $id, $user_id, 'admin', 'active' ); /** * Fires after a space is created. * * @since 1.0.0 * * @param int $id Space ID. * @param int $user_id Creator ID. */ do_action( 'spacebay_space_created', $id, $user_id ); $response = rest_ensure_response( SpaceFormatter::space( SpaceRepository::find( $id ), $user_id ) ); $response->set_status( 201 ); return $response; } /** * Updates a space. Space admins can edit details; only community * managers can archive it or move it to another group. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function update_item( $request ) { $space = $this->visible_space( $request['id'] ); if ( is_wp_error( $space ) ) { return $space; } $user_id = get_current_user_id(); if ( ! SpaceAccess::can_manage( $space, $user_id ) ) { return $this->forbidden(); } $input = $this->space_input( $request ); if ( ! SpaceAccess::is_manager( $user_id ) ) { unset( $input['status'], $input['group_id'], $input['position'] ); } $data = SpaceRepository::clean( $input, false ); if ( is_wp_error( $data ) ) { return $data; } SpaceRepository::update( $space['id'], $data ); /** * Fires after a space is updated. * * @since 1.0.0 * * @param int $id Space ID. * @param array $data Changed fields. */ do_action( 'spacebay_space_updated', $space['id'], $data ); return rest_ensure_response( SpaceFormatter::space( SpaceRepository::find( $space['id'] ), $user_id ) ); } /** * Deletes a space and its memberships. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function delete_item( $request ) { $space = SpaceRepository::find( $request['id'] ); if ( ! $space || 'space' !== $space['type'] ) { return new WP_Error( 'spacebay_space_not_found', __( 'Space not found.', 'spacebay' ), array( 'status' => 404 ) ); } SpaceRepository::delete( $space['id'] ); return rest_ensure_response( array( 'deleted' => true, 'id' => $space['id'], ) ); } /** * Saves a new order of spaces or groups. * * @param WP_REST_Request $request Request. * @return WP_REST_Response */ public function reorder( $request ) { SpaceRepository::reorder( array_map( 'absint', (array) $request['ids'] ) ); return rest_ensure_response( array( 'reordered' => true ) ); } /** * Joins a space, asks to join, or accepts an invitation. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function join( $request ) { $limited = \SpaceBay\Support\RateLimit::hit( 'join', get_current_user_id(), 20 ); if ( is_wp_error( $limited ) ) { return $limited; } $space = $this->visible_space( $request['id'] ); if ( is_wp_error( $space ) ) { return $space; } $result = Membership::join( $space, get_current_user_id() ); if ( is_wp_error( $result ) ) { return $result; } return rest_ensure_response( SpaceFormatter::space( $space, get_current_user_id() ) ); } /** * Leaves a space, cancels a request, or declines an invitation. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function leave( $request ) { $limited = \SpaceBay\Support\RateLimit::hit( 'join', get_current_user_id(), 20 ); if ( is_wp_error( $limited ) ) { return $limited; } $space = $this->visible_space( $request['id'] ); if ( is_wp_error( $space ) ) { return $space; } $user_id = get_current_user_id(); $membership = Membership::get( $space['id'], $user_id ); if ( ! $membership || 'blocked' === $membership['status'] ) { return new WP_Error( 'spacebay_not_member', __( 'You are not a member of this space.', 'spacebay' ), array( 'status' => 400 ) ); } if ( 'admin' === $membership['role'] && 'active' === $membership['status'] && Membership::count_admins( $space['id'] ) <= 1 ) { return new WP_Error( 'spacebay_last_admin', __( 'You are the only admin of this space. Make another member an admin before you leave.', 'spacebay' ), array( 'status' => 400 ) ); } Membership::remove( $space['id'], $user_id ); $fresh = SpaceRepository::find( $space['id'] ); if ( ! SpaceAccess::can_see( $fresh, $user_id ) ) { return rest_ensure_response( array( 'left' => true ) ); } return rest_ensure_response( SpaceFormatter::space( $fresh, $user_id ) ); } /** * Creates a space group. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function create_group( $request ) { $data = SpaceRepository::clean( array( 'title' => $request['title'] ), true ); if ( is_wp_error( $data ) ) { return $data; } $data['type'] = 'group'; $id = SpaceRepository::create( $data, get_current_user_id() ); $response = rest_ensure_response( SpaceFormatter::group( SpaceRepository::find( $id ) ) ); $response->set_status( 201 ); return $response; } /** * Renames a space group. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function update_group( $request ) { $group = SpaceRepository::find( $request['id'] ); if ( ! $group || 'group' !== $group['type'] ) { return new WP_Error( 'spacebay_group_not_found', __( 'Space group not found.', 'spacebay' ), array( 'status' => 404 ) ); } $data = SpaceRepository::clean( array( 'title' => $request['title'] ), true ); if ( is_wp_error( $data ) ) { return $data; } SpaceRepository::update( $group['id'], $data ); return rest_ensure_response( SpaceFormatter::group( SpaceRepository::find( $group['id'] ) ) ); } /** * Deletes a space group. Its spaces stay and become ungrouped. * * @param WP_REST_Request $request Request. * @return WP_REST_Response|WP_Error */ public function delete_group( $request ) { $group = SpaceRepository::find( $request['id'] ); if ( ! $group || 'group' !== $group['type'] ) { return new WP_Error( 'spacebay_group_not_found', __( 'Space group not found.', 'spacebay' ), array( 'status' => 404 ) ); } SpaceRepository::delete( $group['id'] ); return rest_ensure_response( array( 'deleted' => true, 'id' => $group['id'], ) ); } /** * Picks the editable space fields present in the request. * * @param WP_REST_Request $request Request. * @return array */ private function space_input( $request ) { $input = array(); foreach ( array( 'title', 'slug', 'description', 'privacy', 'status', 'group_id', 'icon', 'color', 'who_can_post' ) as $field ) { if ( $request->has_param( $field ) ) { $input[ $field ] = $request->get_param( $field ); } } return $input; } }