[ 'type' => 'string', 'required' => true, 'sanitize_callback' => static fn ( $v ) => (string) $v ] ]; register_rest_route( self::NS, '/slots', [ 'methods' => 'GET', 'callback' => [ self::class, 'list' ], 'permission_callback' => static fn () => current_user_can( 'edit_theme_options' ) || current_user_can( 'manage_woocommerce' ), ] ); register_rest_route( self::NS, '/slots/(?P[a-z0-9_\-]+/[a-z0-9_\-]+)/candidates', [ 'methods' => 'GET', 'callback' => [ self::class, 'candidates' ], 'permission_callback' => [ self::class, 'can' ], 'args' => $slot_arg, ] ); register_rest_route( self::NS, '/slots/(?P[a-z0-9_\-]+/[a-z0-9_\-]+)/pins', [ [ 'methods' => 'GET', 'callback' => [ self::class, 'get_pins' ], 'permission_callback' => [ self::class, 'can' ], 'args' => $slot_arg, ], [ 'methods' => 'PUT', 'callback' => [ self::class, 'put_pins' ], 'permission_callback' => [ self::class, 'can' ], 'args' => $slot_arg + [ 'default' => [ 'type' => 'string', 'required' => false ], 'rules' => [ 'type' => 'array', 'required' => false ], 'rule' => [ 'type' => 'object', 'required' => false ], ], ], ] ); } public static function can( \WP_REST_Request $request ): bool { $def = Registry::instance()->get( (string) $request['slot'] ); return $def ? current_user_can( (string) $def['capability'] ) : false; } public static function list( \WP_REST_Request $request ): \WP_REST_Response { $registry = Registry::instance(); $pins = $registry->pins(); $out = []; foreach ( $registry->all() as $slot => $def ) { $out[] = [ 'slot' => $slot, 'label' => (string) $def['label'], 'kind' => (string) $def['kind'], 'post_types' => (array) $def['post_types'], 'taxonomies' => (array) $def['taxonomies'], 'default' => (string) $def['default'], 'matches' => (array) $def['matches'], 'source' => (string) $def['source'], 'capability' => (string) $def['capability'], 'pins' => $pins[ $slot ] ?? [ 'default' => '', 'rules' => [] ], 'page_id' => 'page' === $def['kind'] ? $registry->bound_page( $def ) : 0, ]; } return rest_ensure_response( $out ); } public static function candidates( \WP_REST_Request $request ): \WP_REST_Response { $candidates = Registry::instance()->candidates( (string) $request['slot'] ); $out = []; foreach ( $candidates as $slug => $c ) { $out[] = [ 'slug' => (string) $slug ] + $c; } return rest_ensure_response( $out ); } public static function get_pins( \WP_REST_Request $request ): \WP_REST_Response { return rest_ensure_response( Registry::instance()->pins()[ (string) $request['slot'] ] ?? [ 'default' => '', 'rules' => [] ] ); } public static function put_pins( \WP_REST_Request $request ) { $registry = Registry::instance(); $slot = (string) $request['slot']; $pins = $registry->pins(); $entry = $pins[ $slot ] ?? [ 'default' => '', 'rules' => [] ]; if ( $request->has_param( 'rule' ) ) { $rule = (array) $request['rule']; $match = (array) ( $rule['match'] ?? [] ); $rules = array_values( array_filter( (array) $entry['rules'], static fn ( $r ) => ( $r['match'] ?? null ) != $match ) ); if ( '' !== (string) ( $rule['template'] ?? '' ) ) { $clean = Registry::sanitize_rule( $rule ); if ( ! $clean ) { return new \WP_Error( 'rooster_invalid_rule', __( 'The rule is not valid.', 'ai-eshop-optimizer' ), [ 'status' => 400 ] ); } $rules[] = $clean; } $entry['rules'] = $rules; } else { if ( $request->has_param( 'default' ) ) { $entry['default'] = (string) $request['default']; } if ( $request->has_param( 'rules' ) ) { $entry['rules'] = (array) $request['rules']; } } $pins[ $slot ] = $entry; if ( ! $registry->set_pins( $pins ) ) { return new \WP_Error( 'rooster_pins_not_saved', __( 'The pins could not be saved.', 'ai-eshop-optimizer' ), [ 'status' => 500 ] ); } return rest_ensure_response( $registry->pins()[ $slot ] ?? [ 'default' => '', 'rules' => [] ] ); } }