# booktics/1.0.29/core/team-member/controllers/team-member-controller.php

Booktics – Appointment Booking Calendar for Service Businesses, version 1.0.29. 732 lines.

- Page: https://pluginprobe.com/plugins/booktics/1.0.29/code/core/team-member/controllers/team-member-controller.php
- Raw: https://pluginprobe.com/plugins/booktics/1.0.29/raw/core/team-member/controllers/team-member-controller.php
- Modified: 2026-10-11T10:01:32+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/booktics/1.0.29/code/core/team-member/controllers/team-member-controller.php#L10-L20`.

```php
<?php

namespace Booktics\Team_Member\Controllers;

use Booktics\Abstracts\Base_Rest_Controller;
use Booktics\Abstracts\User_Model;
use Booktics\Models\Service_Model;
use Booktics\Models\Team_Member_Model;
use Booktics\Services\Schedule_Manager;
use Booktics\Services\Schedule_Record;
use Booktics\Team_Member\Handlers\Team_Member_Event_Handler;
use Exception;
use WP_Error;
use WP_HTTP_Response;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Team member controller
 *
 * @package Booktics/Team_Member
 */
class Team_Member_Controller extends Base_Rest_Controller {

    /**
     * Endpoint namespace
     *
     * @var string
     */
    protected $namespace = 'booktics/v1';

    /**
     * Route name
     *
     * @var string
     */
    protected $base = 'team-members';

    /**
     * Register routes
     *
     * @return  void
     */
    public function register_routes(): void {
        register_rest_route(
            $this->namespace, $this->base, array(
                array(
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => array( $this, 'create_item' ),
                    'permission_callback' => array(
                        $this,
                        'create_team_member_permission',
                    ),
                ),
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_items' ),
                    'permission_callback' => array(
                        $this,
                        'get_team_member_permission',
                    ),
                ),
                array(
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => array( $this, 'delete_items' ),
                    'permission_callback' => array(
                        $this,
                        'delete_team_member_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, $this->base.'/profile', array(
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_profile' ),
                    'permission_callback' => array(
                        $this,
                        'get_team_member_profile_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)', array(
                array(
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => array( $this, 'update_item' ),
                    'permission_callback' => array(
                        $this,
                        'update_team_member_permission',
                    ),
                    'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
                ),
                array(
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => array( $this, 'delete_item' ),
                    'permission_callback' => array(
                        $this,
                        'delete_team_member_permission',
                    ),
                ),
                array(
                    'method'              => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_item' ),
                    'permission_callback' => array(
                        $this,
                        'get_team_member_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)/reinvites', array(
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'reinvite' ),
                    'permission_callback' => array(
                        $this,
                        'create_team_member_permission',
                    ),
                ),
            )
        );
    }

    /**
     * Check if a given request has access to create items.
     *
     * @param $request
     *
     * @return mixed
     */
    public function create_team_member_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Prepare and validate team member data
     *
     * @param $request
     *
     * @return array|bool|WP_Error
     */
    protected function prepare_team_member_data( $request ) {
        $input_data = json_decode( $request->get_body(), true );

        $validate = booktics_validate(
            $input_data, array(
                'first_name' => array(
                    'required',
                ),
                'email'      => array(
                    'required',
                    'unique:wp_users,email',
                ),
            )
        );

        if ( is_wp_error( $validate ) ) {
            return $validate;
        }

        return array(
            'first_name'             => $input_data['first_name'],
            'last_name'              => $input_data['last_name'],
            'user_email'             => $input_data['email'],
            'user_login'             => User_Model::generate_username( $input_data['email'] ),
            'phone'                  => $input_data['phone'],
            'email'                  => $input_data['email'],
            'status'                 => 'pending',
            'enable_custom_holiday'  => $input_data['enable_custom_holiday'] ?? false,
            'enable_custom_schedule' => $input_data['enable_custom_schedule'] ?? false,
            'enable_custom_availability' => $input_data['enable_custom_availability'] ?? false,
        );
    }

    /**
     * Store new team member data
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function create_item( $request ) {
        $prepared_data = $this->prepare_team_member_data( $request );
        if ( is_wp_error( $prepared_data ) ) {
            return $this->error(
                $prepared_data->get_error_message(),
                422,
                'Validation',
                $prepared_data->errors
            );
        }

        $all_team_members = ( new Team_Member_Model() )->all( array() );
        if ( ! booktics_is_pro_active() && isset( $all_team_members['total'] ) && $all_team_members['total'] >= 3 ) {
            return $this->error(
                'Team member limit reached for free plan.',
                422,
                'Validation'
            );
        }

        $team_member = new Team_Member_Model();
        $created     = $team_member->save( $prepared_data );

        $image_url = $request->get_param( 'image' );
        if ( $image_url ) {
            $team_member->update( array( 'image' => esc_url_raw( $image_url ) ) );
        }

        if ( ! $created ) {
            return $this->error( __( 'Team member create error', 'booktics' ), 500 );
        }

        do_action( 'booktics_team_member_created', $team_member, $request );

        ( new Team_Member_Event_Handler() )->on_team_member_create( $created );

        return $this->response( $team_member->find( $created )->to_array(), __( 'Successfully created team member', 'booktics' ), 201 );
    }

    /**
     * Check if a given request has access to get all team member
     *
     * @param $request
     *
     * @return bool
     */
    public function get_team_member_permission( $request ) {
        // Admins and team members always have access
        if ( booktics_is_admin_or_team_member() ) {
            return true;
        }

        $nonce = $request->get_header( 'X-WP-Nonce' );
        if ( $nonce && wp_verify_nonce( $nonce, 'wp_rest' ) ) {
            return true;
        }

        return new WP_Error(
            'booktics_forbidden',
            __( 'You are not allowed to view team member data.', 'booktics' ),
            array( 'status' => 403 )
        );
    }

    /**
     * Get paginated list items for team member
     *
     * @param $request
     *
     * @return WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function get_items( $request ) {
        $per_page    = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
        $paged       = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
        $first_name  = ! empty( $request['first_name'] ) ? sanitize_text_field( $request['first_name'] ) : '';
        $last_name   = ! empty( $request['last_name'] ) ? sanitize_text_field( $request['last_name'] ) : '';
        $email       = ! empty( $request['email'] ) ? sanitize_email( $request['email'] ) : '';
        $phone       = ! empty( $request['phone'] ) ? sanitize_text_field( $request['phone'] ) : '';
        $user_name   = ! empty( $request['user_name'] ) ? sanitize_text_field( $request['user_name'] ) : '';
        $status      = ! empty( $request['status'] ) ? sanitize_text_field( $request['status'] ) : '';
        $service_id  = ! empty( $request['service_id'] ) ? sanitize_text_field( $request['service_id'] ) : '';
        $search      = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
        $location_id = ! empty( $request['location_id'] ) ? intval( $request['location_id'] ) : 0;

        if ( $location_id ) {
            $location_teams = get_post_meta( $location_id, 'teams', true );
            $team_ids       = array_map( 'intval', is_array( $location_teams ) ? $location_teams : array() );

            $items = array();
            foreach ( $team_ids as $tid ) {
                $member = ( new Team_Member_Model() )->find( $tid );
                if ( $member ) {
                    $items[] = $member->to_array();
                }
            }

            $total = count( $items );
            $offset = ( $paged - 1 ) * $per_page;
            $items  = array_slice( $items, $offset, $per_page );

            return $this->response(
                array(
                    'items'        => $items,
                    'total'        => $total,
                    'per_page'     => $per_page,
                    'current_page' => $paged,
                    'last_page'    => $per_page > 0 ? (int) ceil( $total / $per_page ) : 0,
                ),
                __( 'Team members fetched for location', 'booktics' ),
                200
            );
        }

        $team_members = ( new Team_Member_Model() )
            ->when(
                $first_name, function ( $q, $v ) {
					$q->where( 'first_name', $v, 'LIKE' );
				}
            )
            ->when(
                $last_name, function ( $q, $v ) {
					$q->where( 'last_name', $v, 'LIKE' );
				}
            )
            ->when(
                $email, function ( $q, $v ) {
					$q->where( 'user_email', $v, 'LIKE' );
				}
            )
            ->when(
                $phone, function ( $q, $v ) {
					$q->where( 'phone', $v, 'LIKE' );
				}
            )
            ->when(
                $user_name, function ( $q, $v ) {
					$q->where( 'user_login', $v, 'LIKE' );
				}
            )
            ->when(
                $service_id, function ( $q, $v ) {
					$q->where( 'services', $v, 'LIKE' );
				}
            )
            ->when(
                $status, function ( $q, $v ) {
					$q->where( 'status', $v, 'LIKE' );
				}
            )
            ->when(
                $search, function ( $q, $v ) {
					$q->where( 'first_name', $v, 'LIKE' )
					->or_where( 'last_name', $v, 'LIKE' )
					->or_where( 'phone', $v, 'LIKE' );
				}
            )->all(
                array(
                    'paged'  => $paged,
                    'number' => $per_page,
                )
            );

        // Mask sensitive private information fields based on user role
        $is_admin = current_user_can( 'manage_options' );
        $is_team_member = booktics_is_team_member();
        $current_user_id = get_current_user_id();

        foreach ( $team_members['items'] as &$team_member ) {
            $team_member_array = is_array( $team_member ) ? $team_member : ( is_object( $team_member ) ? get_object_vars( $team_member ) : array() );
            
            $team_member_wp_user_id = $team_member_array['wp_user_id'] ?? null;
            $is_viewing_self = ( (int)$team_member_wp_user_id == (int)$current_user_id );

            if ( ! $is_admin && ( ! $is_team_member || ! $is_viewing_self ) ) {
                // Mask sensitive fields with empty string for API consistency
                $team_member_array['user_email'] = '';
                $team_member_array['email'] = '';
                $team_member_array['phone'] = '';
                $team_member_array['user_login'] = '';
                $team_member_array['wp_user_id'] = '';
                $team_member = $team_member_array;
            }
        }

        return $this->response( $team_members );
    }

    /**
     * Checks if a given request has access to update a team member.
     *
     * @param $request
     *
     * @return bool
     */
    public function update_team_member_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Updates a single team member.
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     * @throws Exception
     */
    public function update_item( $request ) {
        if ( ! isset( $request['id'] ) ) {
            return $this->error( __( 'Invalid team member id', 'booktics' ), 400 );
        }
        $team_member = ( new Team_Member_Model( $request['id'] ) );

        if ( isset( $request['password'] ) ) {
            $current_password = sanitize_text_field( $request['current_password'] ?? '' );
            $new_password     = sanitize_text_field( $request['password'] ?? '' );
            $confirm_password = sanitize_text_field( $request['confirm_password'] ?? '' );

            $validate = booktics_validate(
                array(
                    'current_password' => $current_password,
                    'password'         => $new_password,
                    'confirm_password' => $confirm_password,
                ), array(
                    'current_password' => array( 'required' ),
                    'password'         => array( 'required' ),
                    'confirm_password' => array( 'required' ),
                )
            );

            if ( is_wp_error( $validate ) ) {
                return $validate;
            }

            $user = wp_get_current_user();

            if ( ! wp_check_password( $current_password, $user->user_pass, $user->ID ) ) {
                return $this->error( __( 'Current password is incorrect.', 'booktics' ), 401 );
            }

            wp_set_password( $new_password, $user->ID );

            return $this->response(
                $team_member->find( $request['id'] )->to_array(),
                __( 'Successfully updated team member', 'booktics' )
            );
        }

        $prepared_data = $this->prepare_team_member_update( $request );
        if ( is_wp_error( $prepared_data ) ) {
            return $this->error( $prepared_data->get_error_message(), 422, 'Validation', $prepared_data->errors );
        }

        $updated = $team_member->update( $prepared_data );
        // First handle adding team member to new services
        foreach ( $prepared_data['services'] as $service ) {
            $service_model       = Service_Model::find( $service );
            $service_team_member = $service_model->team_member == '' ? array() : $service_model->team_member;
            if ( ! in_array( (int) $team_member->id, $service_team_member ) ) {
                $service_team_member[] = (int) $request['id'];
                if ( $service_model ) {
                    $service_model->update( array( 'team_member' => $service_team_member ) );
                }
            }
        }

        // Then remove team member from services they're no longer assigned to
        $services = Service_Model::query()
                                ->where( 'team_member', $request['id'], 'LIKE' )
                                ->where_not_ids( $prepared_data['services'] )
                                ->get();

        foreach ( $services as $service ) {
            if ( $service->team_member ) {
                $service_team_members = array_diff( $service->team_member, array( (int) $request['id'] ) );
                Service_Model::find( $service->id )->update( array( 'team_member' => $service_team_members ) );
            }
        }
        $image_url = $request->get_param( 'image' );
        if ( $image_url ) {
            $team_member->update( array( 'image' => esc_url_raw( $image_url ) ) );
        }

        if ( ! $updated ) {
            return $this->error( __( 'Team member update error', 'booktics' ), 500 );
        }
        if ( isset( $request['schedule'] ) || isset( $request['custom'] ) || isset( $request['holiday'] ) ) {
            $input_data           = json_decode( $request->get_body(), true );
            $team_member_schedule = Schedule_Manager::make( $input_data );
            $team_member_schedule->with_team_member_id( $request['id'] );
            $team_member_schedule->save();
        }

        do_action( 'booktics_team_member_updated', $team_member, $request );

        $team_member             = $team_member->find( $request['id'] )->to_array();
        $schedule_record         = ( new Schedule_Record() )->get_schedules( $request['id'] );
        $team_member['schedule'] = empty( $schedule_record['schedule'] )
            ? (object) array() : $schedule_record['schedule'];
        $team_member['custom']   = $schedule_record['custom'];
        $team_member['holiday']  = $schedule_record['holiday'];

        return $this->response(
            $team_member,
            __( 'Successfully updated team member', 'booktics' )
        );
    }

    /**
     * Prepare data for team member update
     *
     * @param $request
     *
     * @return array|bool|WP_Error
     */
    public function prepare_team_member_update( $request ) {
        $input_data              = json_decode( $request->get_body(), true );
        $validation_rules        = array(
            'first_name' => array(
                'required',
            ),
            'email'      => array(
                'required',
                'unique:wp_users,email,' . $request['id'],
            ),
        );
        $update_validation_rules = array();
        $validated_data          = array();
        foreach ( $input_data as $key => $value ) {
            $validated_data[ $key ] = $value;
            if ( ! isset( $validation_rules[ $key ] ) ) {
                continue;
            }
            $update_validation_rules[ $key ] = $validation_rules[ $key ];
            if ( 'email' == $key ) {
                $update_validation_rules[ $key ] = $validation_rules[ $key ];
                $validated_data['user_email']    = sanitize_email( $value );
            }
        }
        $validate = booktics_validate( $input_data, $update_validation_rules );

        if ( is_wp_error( $validate ) ) {
            return $validate;
        }

        $validated_data['services'] = ! empty( $input_data['services'] ) ? $input_data['services'] : array();
        //$validated_data['status']   = 'active';

        return $validated_data;
    }

    /**
     * Checks if a given request has access to delete a team member.
     *
     * @param $request
     *
     * @return bool
     */
    public function delete_team_member_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Delete a team member
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function delete_item( $request ) {
        $id = intval( $request['id'] );

        $team_member = new Team_Member_Model( $id );
        $previous    = $team_member->find( $id );

        // Remove team member from assigned services
        $services = Service_Model::query()->where( 'team_member', $id, 'LIKE' )->get();
        foreach ( $services as $service ) {
            if ( $service->team_member ) {
                $service_team_members = array_diff( $service->team_member, array( $id ) );
                Service_Model::find( $service->id )->update( array( 'team_member' => $service_team_members ) );
            }
        }

        $deleted = $team_member->delete();

        if ( ! $deleted ) {
            return $this->error( __( 'Team member delete error', 'booktics' ), 500 );
        }

        do_action( 'booktics_team_members_deleted', $request );

        return $this->response( $previous, __( 'Successfully deleted team member', 'booktics' ) );
    }

    /**
     * Delete multiple team member
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response
     */
    public function delete_items( $request ) {
        $ids = ! empty( $request['ids'] ) ? $request['ids'] : array();

        if ( ! $ids ) {
            return $this->error( __( 'Team member ids cannot be empty', 'booktics' ), 500 );
        }
        $count = 0;
        foreach ( $ids as $id ) {
            $team_member = new Team_Member_Model( $id );
            if ( $team_member->delete() ) {
                ++$count;
            }
        }
        if ( 0 == $count ) {
            return $this->error( __( 'Team member delete error', 'booktics' ), 500 );
        }

        do_action( 'booktics_team_members_deleted', $request );

        return $this->response( array(), __( 'Successfully deleted team member', 'booktics' ) );
    }

    /**
     * Get single team member
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function get_item( $request ) {
        $id          = intval( $request['id'] );
        $team_member = new Team_Member_Model( $id );
        $team_member = $team_member->find( $id );
        if ( ! $team_member ) {
            return $this->error( __( 'Team member not found', 'booktics' ), 404 );
        }
        $team_member             = $team_member->to_array();
        $schedule_record         = ( new Schedule_Record() )->get_schedules( $request['id'] );
        $team_member['schedule'] = ! empty( $schedule_record['schedule'] ) ? $schedule_record['schedule'] : $team_member['schedule'];
        $team_member['custom']   = ! empty( $schedule_record['custom'] ) ? $schedule_record['custom'] : $team_member['custom'];
        $team_member['holiday']  = ! empty( $schedule_record['holiday'] ) ? $schedule_record['holiday'] : $team_member['holiday'];

        // Add filter to allow modifying team member data
        $team_member = apply_filters( 'booktics_team_member_data', $team_member, $id );

        // Mask PII fields based on user role
        $is_admin = current_user_can( 'manage_options' );
        $is_team_member = booktics_is_team_member();
        $current_user_id = get_current_user_id();
        
        // Get the team member's WP user ID to check if viewing own record
        $team_member_wp_user_id = $team_member['wp_user_id'] ?? null;
        $is_viewing_self = ( $team_member_wp_user_id == $current_user_id );

        // If not admin and (not team member OR viewing someone else's record)
        if ( ! $is_admin && ( ! $is_team_member || ! $is_viewing_self ) ) {
            // Mask sensitive fields with empty string for API consistency
            $team_member['user_email'] = '';
            $team_member['email'] = '';
            $team_member['phone'] = '';
            $team_member['user_login'] = '';
            $team_member['wp_user_id'] = '';
        }

        return $this->response( $team_member, __( 'Team member successfully fetched', 'booktics' ) );
    }

    /**
     * Check if a given request has access to create items.
     *
     * @param $request
     *
     * @return mixed
     */
    public function create_team_member_schedule_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Team member reinvite
     *
     * @param $request
     *
     * @return array|WP_HTTP_Response
     */
    public function reinvite( $request ) {
        $id = intval( $request['id'] );

        $invite = ( new Team_Member_Event_Handler() )->on_team_member_create( $id );

        if ( ! $invite ) {
            return $this->error( __( 'Team member reinvite error', 'booktics' ) );
        }

        return $this->response( array(), __( 'Team member reinvited successfully', 'booktics' ) );
    }

    /**
     * Check if a given request has access to get team member profile.
     *
     * @return bool
     */
    public function get_team_member_profile_permission() {
        // Team members can view their own profile
        if ( booktics_is_admin_or_team_member() ) {
            return true;
        }

        return new WP_Error(
            'booktics_forbidden',
            __( 'You are not allowed to view team member profile.', 'booktics' ),
            array( 'status' => 403 )
        );
    }

    /**
     * Get team member profile
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function get_profile( $request ) {
        $id = get_current_user_id();

        $team_member = new Team_Member_Model( $id );
        $team_member = $team_member->find( $id );
        if ( ! $team_member ) {
            return $this->error( __( 'Team member not found', 'booktics' ), 404 );
        }
        $team_member = $team_member->to_array();
        $schedule_record         = ( new Schedule_Record() )->get_schedules( $id );
        $team_member['schedule'] = ! empty( $schedule_record['schedule'] ) ? $schedule_record['schedule'] : $team_member['schedule'];
        $team_member['custom']   = ! empty( $schedule_record['custom'] ) ? $schedule_record['custom'] : $team_member['custom'];
        $team_member['holiday']  = ! empty( $schedule_record['holiday'] ) ? $schedule_record['holiday'] : $team_member['holiday'];

        // Add filter to allow modifying team member data
        $team_member = apply_filters( 'booktics_team_member_data', $team_member, $id );

        return $this->response( $team_member, __( 'Team member successfully fetched', 'booktics' ) );
    }
}

```
