\d+)'; /** * The module's Forms service — owns sanitize/save/read logic. Injected * from Form_Builder::init() (the same instance already wired to the * module's other hooks) rather than constructed here, since * `save_form_data()`/`sanitize_fields_for_preview()`/`build_form_settings()` * are instance methods, not static. * * @var Form_Builder_Forms */ private $forms; /** * The module orchestrator — owns `render_form_markup()`, the shared * fields/settings-to-HTML renderer the preview route reuses so the * preview matches the real frontend exactly. * * @var Form_Builder */ private $form_builder; /** * Constructor. * * @param Form_Builder_Forms $forms Forms service instance. * @param Form_Builder $form_builder Module orchestrator instance. */ public function __construct( Form_Builder_Forms $forms, Form_Builder $form_builder ) { $this->forms = $forms; $this->form_builder = $form_builder; add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] ); } /** * Register the form-builder REST routes. * * @return void */ public function register_rest_routes() { register_rest_route( self::REST_NAMESPACE, self::REST_BASE, [ [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_form' ], 'permission_callback' => [ $this, 'check_permission' ], 'args' => [ 'id' => [ 'validate_callback' => function ( $value ) { return is_numeric( $value ); }, ], ], ], [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'save_form' ], 'permission_callback' => [ $this, 'check_permission' ], 'args' => [ 'id' => [ 'validate_callback' => function ( $value ) { return is_numeric( $value ); }, ], ], ], ] ); register_rest_route( self::REST_NAMESPACE, self::REST_BASE . '/preview', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'preview_form' ], 'permission_callback' => [ $this, 'check_permission' ], 'args' => [ 'id' => [ 'validate_callback' => function ( $value ) { return is_numeric( $value ); }, ], ], ] ); } /** * Shared capability check for every route in this controller. * * @return bool */ public function check_permission() { return current_user_can( 'manage_options' ); } /** * Resolve and validate the `adminoptim-form` post for a request's `id`. * * @param WP_REST_Request $request Request object. * * @return \WP_Post|WP_Error */ private function get_form_post( WP_REST_Request $request ) { $form_id = absint( $request->get_param( 'id' ) ); $post = $form_id ? get_post( $form_id ) : null; if ( ! $post || Form_Builder::ADMIN_POST_TYPE !== $post->post_type ) { return new WP_Error( 'aofb_not_found', __( 'This form could not be found.', 'admin-optimizer' ), [ 'status' => 404 ] ); } return $post; } /** * GET /form-builder/{id} — the form's current saved state, the filtered * field-type registry, and enough context for the builder app to boot. * * @param WP_REST_Request $request Request object. * * @return array|WP_Error */ public function get_form( WP_REST_Request $request ) { $post = $this->get_form_post( $request ); if ( is_wp_error( $post ) ) { return $post; } return [ 'id' => (int) $post->ID, 'title' => (string) $post->post_title, 'status' => (string) $post->post_status, 'fields' => Form_Builder_Forms::get_form_fields( $post->ID ), 'settings' => Form_Builder_Forms::get_form_settings( $post->ID ), ]; } /** * POST /form-builder/{id} — validate, sanitize, and persist a save payload. * * @param WP_REST_Request $request Request object. * * @return array|WP_Error */ public function save_form( WP_REST_Request $request ) { $post = $this->get_form_post( $request ); if ( is_wp_error( $post ) ) { return $post; } $payload = $request->get_json_params(); $payload = is_array( $payload ) ? $payload : []; return $this->forms->save_form_data( (int) $post->ID, $payload ); } /** * POST /form-builder/{id}/preview — sanitize unsaved fields/settings the * same way a save would (without persisting anything), then render them * through the same markup path the real frontend uses, so the preview * modal matches the published form exactly, including per-form style * variables. * * @param WP_REST_Request $request Request object. * * @return array|WP_Error */ public function preview_form( WP_REST_Request $request ) { $post = $this->get_form_post( $request ); if ( is_wp_error( $post ) ) { return $post; } $payload = $request->get_json_params(); $payload = is_array( $payload ) ? $payload : []; $raw_fields = isset( $payload['fields'] ) && is_array( $payload['fields'] ) ? array_values( $payload['fields'] ) : []; $fields = $this->forms->sanitize_fields_for_preview( $raw_fields ); $raw_settings = isset( $payload['settings'] ) && is_array( $payload['settings'] ) ? $payload['settings'] : []; $settings = wp_parse_args( $raw_settings, Form_Builder_Forms::get_default_form_settings() ); $html = $this->form_builder->render_form_markup( $fields, $settings, [], [], (int) $post->ID, true ); return [ 'html' => $html, 'fields' => $fields, 'settings' => $settings, ]; } }