PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / trunk
Secure Custom Fields vtrunk
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / src / AI / Abilities / Abilities.php
secure-custom-fields / src / AI / Abilities Last commit date
Abilities.php 2 months ago AbstractAbilityGroup.php 2 months ago FieldGroup.php 1 month ago PostType.php 2 months ago SCF_REST_Ability.php 2 months ago Taxonomy.php 1 month ago
Abilities.php
197 lines
1 <?php
2 /**
3 * ACF 6.8.0 feature port.
4 *
5 * @package wordpress/secure-custom-fields
6 */
7
8 // phpcs:disable -- Upstream ACF 6.8.0 feature-port files are kept close to source.
9
10 namespace SCF\AI\Abilities;
11
12 use WP_REST_Request;
13
14 // Exit if accessed directly.
15 defined( 'ABSPATH' ) || exit;
16
17 /**
18 * The ACF Abilities API integration.
19 *
20 * Extends the WordPress Abilities API to expose field groups, post types,
21 * taxonomies, and options pages when the "Allow AI Access" setting is enabled.
22 */
23 class Abilities {
24
25 /**
26 * Array of registered ability group instances
27 *
28 * @var array
29 */
30 private array $ability_groups = array();
31
32 /**
33 * Constructs the class.
34 *
35 * @since 6.8.0
36 *
37 * @return void
38 */
39 public function __construct() {
40 $this->init();
41 }
42
43 /**
44 * Initialize the Abilities API integration.
45 *
46 * @since 6.8.0
47 *
48 * @return void
49 */
50 public function init() {
51 // Register ability group classes.
52 $this->register_ability_group( 'field_group', FieldGroup::class );
53 $this->register_ability_group( 'post_type', PostType::class );
54 $this->register_ability_group( 'taxonomy', Taxonomy::class );
55
56 // Register categories (v0.3.0+ requirement).
57 add_action( 'wp_abilities_api_categories_init', array( $this, 'register_categories' ) );
58
59 // Register abilities.
60 add_action( 'wp_abilities_api_init', array( $this, 'register_abilities' ) );
61
62 // Fix for WordPress 6.9 Abilities API bug: parse JSON from query parameters.
63 add_filter( 'rest_request_before_callbacks', array( $this, 'parse_abilities_json_input' ), 10, 3 );
64 }
65
66 /**
67 * Register an ability group class.
68 *
69 * @since 6.8.0
70 *
71 * @param string $key Unique key for this ability group.
72 * @param string $class_name Fully qualified class name.
73 * @param array $args Optional constructor arguments.
74 * @return void
75 */
76 private function register_ability_group( $key, $class_name, $args = array() ) {
77 if ( ! class_exists( $class_name ) ) {
78 return;
79 }
80
81 // Instantiate the class with any provided arguments.
82 if ( ! empty( $args ) ) {
83 $this->ability_groups[ $key ] = new $class_name( ...$args );
84 } else {
85 $this->ability_groups[ $key ] = new $class_name();
86 }
87 }
88
89 /**
90 * Get an ability group instance by key
91 *
92 * @since 6.8.0
93 *
94 * @param string $key The ability group key.
95 * @return object|null The ability group instance or null if not found.
96 */
97 private function get_ability_group( $key ) {
98 return $this->ability_groups[ $key ] ?? null;
99 }
100
101 /**
102 * Register Ability Categories
103 *
104 * @since 6.8.0
105 *
106 * @return void
107 */
108 public function register_categories() {
109 if ( ! function_exists( 'wp_register_ability_category' ) ) {
110 return;
111 }
112
113 // ACF Field Management category.
114 wp_register_ability_category(
115 'acf-field-management',
116 array(
117 'label' => __( 'SCF Field Management', 'secure-custom-fields' ),
118 'description' => __( 'Abilities for managing Secure Custom Fields field groups and field data.', 'secure-custom-fields' ),
119 )
120 );
121
122 // WordPress Content Discovery category.
123 wp_register_ability_category(
124 'wordpress-content-discovery',
125 array(
126 'label' => __( 'WordPress Content Discovery', 'secure-custom-fields' ),
127 'description' => __( 'Abilities for discovering WordPress content types, taxonomies, and structure.', 'secure-custom-fields' ),
128 )
129 );
130 }
131
132 /**
133 * Register Abilities for ACF
134 *
135 * @since 6.8.0
136 *
137 * @return void
138 */
139 public function register_abilities() {
140 if ( ! function_exists( 'wp_register_ability' ) ) {
141 return;
142 }
143
144 // Register abilities from all registered ability groups.
145 foreach ( $this->ability_groups as $ability_group ) {
146 if ( method_exists( $ability_group, 'register_abilities' ) ) {
147 $ability_group->register_abilities();
148 }
149 }
150 }
151
152 /**
153 * Parse JSON input from query parameters for Abilities API
154 *
155 * WordPress 6.9's Abilities API REST controller doesn't parse JSON strings
156 * from query parameters in GET requests. This filter fixes that by detecting
157 * JSON strings in the 'input' parameter and parsing them into objects/arrays.
158 *
159 * @since 6.8.0
160 *
161 * @param mixed $response Response object.
162 * @param array $handler Route handler info.
163 * @param WP_REST_Request $request Request object.
164 * @return mixed
165 */
166 public function parse_abilities_json_input( $response, $handler, $request ) {
167 // Only process ACF abilities.
168 $route = $request->get_route();
169 if ( strpos( $route, '/wp-abilities/v1/abilities/acf/' ) !== 0 ) {
170 return $response;
171 }
172
173 // Only process GET and DELETE requests (POST uses JSON body which is already parsed).
174 if ( ! in_array( $request->get_method(), array( 'GET', 'DELETE' ), true ) ) {
175 return $response;
176 }
177
178 // Get the input query parameter.
179 $input = $request->get_param( 'input' );
180
181 // If input is a string that looks like JSON, try to parse it.
182 if ( is_string( $input ) && ! empty( $input ) ) {
183 $first_char = substr( trim( $input ), 0, 1 );
184 // Check if it starts with { or [ (JSON object or array).
185 if ( in_array( $first_char, array( '{', '[' ), true ) ) {
186 $parsed = json_decode( $input, true );
187 if ( json_last_error() === JSON_ERROR_NONE ) {
188 // Successfully parsed JSON - update the request parameter.
189 $request->set_param( 'input', $parsed );
190 }
191 }
192 }
193
194 return $response;
195 }
196 }
197