getInputs(); $name = isset( $inputs['name'] ) ? "\"{$inputs['name']}\"" : '(unnamed)'; $type = ! empty( $inputs['type'] ) ? $inputs['type'] : 'one-to-one'; $summary = "Create {$type} meeting {$name}"; if ( ! empty( $inputs['duration_minutes'] ) ) { $summary .= " ({$inputs['duration_minutes']} min)"; } $staff = $this->staff_names( array_map( 'intval', (array) ( $inputs['staff_ids'] ?? [] ) ) ); $names = array_filter( array_column( $staff, 'name' ) ); if ( $names ) { $summary .= ' for ' . implode( ', ', $names ); } return $summary; } public function get_approval_inputs(): array { $inputs = $this->getInputs(); if ( isset( $inputs['staff_ids'] ) ) { $staff = $this->staff_names( array_map( 'intval', (array) $inputs['staff_ids'] ) ); $inputs['staff_ids'] = implode( ', ', array_map( fn( $s ) => sprintf( '%s (ID: %d)', $s['name'], $s['id'] ), $staff ) ); } if ( isset( $inputs['available_days'] ) && is_array( $inputs['available_days'] ) ) { $inputs['available_days'] = implode( ', ', $inputs['available_days'] ); } return $inputs; } public function __invoke( string $name, array $staff_ids, ?string $type = null, ?int $duration_minutes = null, ?float $price = null, ?string $description = null, ?string $location_type = null, ?string $location_value = null, ?array $available_days = null, ?string $start_time = null, ?string $end_time = null, ?string $timezone = null, ?int $capacity = null, ?string $visibility = null, ?array $category_ids = null ): array { $check = $this->guard_admin(); if ( $check ) { return $check; } if ( '' === trim( $name ) ) { return [ 'error' => 'name is required.' ]; } $type = $type ?? 'one-to-one'; if ( ! in_array( $type, self::MEETING_TYPES, true ) ) { return [ 'error' => "Invalid type '{$type}'. Accepted values: " . implode( ', ', self::MEETING_TYPES ) . '.' ]; } $location_type = $location_type ?? 'in-person-meeting'; if ( ! in_array( $location_type, self::SIMPLE_LOCATION_TYPES, true ) ) { return [ 'error' => "Invalid location_type '{$location_type}'. Accepted values: " . implode( ', ', self::SIMPLE_LOCATION_TYPES ) . '.' ]; } $visibility = $visibility ?? 'enabled'; if ( ! in_array( $visibility, self::VISIBILITY, true ) ) { return [ 'error' => "Invalid visibility '{$visibility}'. Accepted values: " . implode( ', ', self::VISIBILITY ) . '.' ]; } $staff = $this->sanitize_staff_ids( $staff_ids ); if ( empty( $staff ) ) { return [ 'error' => 'At least one valid staff member (an existing WP user ID) is required in staff_ids.' ]; } try { $start = trim( (string) ( $start_time ?? '' ) ); $end = trim( (string) ( $end_time ?? '' ) ); // Reject unrecognized available_days instead of silently falling back to Mon-Fri. $requested_days = (array) ( $available_days ?? [] ); if ( ! empty( $requested_days ) && empty( $this->normalize_weekdays( $requested_days ) ) ) { return [ 'error' => 'available_days contained no recognized weekday. Use: Mon, Tue, Wed, Thu, Fri, Sat, Sun (full names also accepted).' ]; } $schedule = $this->build_default_schedule( $staff, $requested_days, '' !== $start ? $start : '09:00 am', '' !== $end ? $end : '05:00 pm' ); $body = [ 'name' => sanitize_text_field( $name ), 'type' => $type, 'staff' => $staff, 'locations' => [ [ 'location_type' => $location_type, 'location' => sanitize_text_field( (string) ( $location_value ?? '' ) ), ], ], 'duration' => max( 1, (int) ( $duration_minutes ?? 30 ) ) . ' min', 'schedule' => $schedule, 'price' => (float) ( $price ?? 0 ), 'timezone' => $this->resolve_timezone( $timezone ), 'visibility' => $visibility, 'capacity' => max( 1, (int) ( $capacity ?? 1 ) ), ]; if ( null !== $description ) { $body['description'] = sanitize_text_field( $description ); } if ( null !== $category_ids ) { $body['categories'] = $this->sanitize_id_list( $category_ids ); } $request = $this->build_request( 'POST', '/timetics/v1/appointments', [], $body ); if ( is_array( $request ) ) { return $request; } $controller = \Timetics\Core\Appointments\Api_Appointment::instance(); $data = $this->extract_response( $controller->create_item( $request ), 'Failed to create meeting.' ); if ( isset( $data['error'] ) ) { return $data; } $id = (int) ( $data['id'] ?? 0 ); if ( $id <= 0 ) { Log::error( 'Timetics_Create_Meeting_Tool got a success response without an ID', [ 'data_keys' => array_keys( $data ) ] ); return [ 'error' => 'The meeting may not have been created; Timetics returned no ID. Please verify in Timetics before retrying.' ]; } return [ 'success' => true, 'meeting_id' => $id, 'meeting' => $this->shape_meeting( $data, true ), 'message' => "Meeting \"{$name}\" created successfully (ID: {$id}).", ]; } catch ( \Throwable $e ) { Log::error( 'Timetics_Create_Meeting_Tool error', [ 'error' => $e->getMessage() ] ); return [ 'error' => 'Failed to create meeting: ' . $e->getMessage() ]; } } /** * Sanitise staff IDs to a list of existing, unique WP user IDs. * * @param array $staff_ids Raw staff IDs from the caller. * @return array */ private function sanitize_staff_ids( array $staff_ids ): array { $ids = []; foreach ( $staff_ids as $id ) { $id = (int) $id; if ( $id > 0 && get_userdata( $id ) ) { $ids[] = $id; } } return array_values( array_unique( $ids ) ); } }