PluginProbe ʕ •ᴥ•ʔ
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback / 5.0
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback v5.0
5.1.3 5.1.2 5.1.1 5.1 5.0 trunk 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 3.2.0 3.2.1 3.22 3.22.1 3.22.2 3.22.3 3.22.4 3.22.5 3.22.6 3.3.0 3.3.1 3.3.2 3.3.2.1 3.3.2.2 3.3.3 3.30 3.31 3.32 3.4 3.4.1 3.4.3 3.4.4 3.5 3.5.1 3.6 3.6.1 3.7 3.8 3.9 3.9.1 3.9.2 3.9.3 3.9.4 3.9.6 3.9.6.1 4.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.2 4.2.1 4.2.2 4.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.4
atarim-visual-collaboration / third-party / content-model / acpt / class-avcf-abilities-acpt.php
atarim-visual-collaboration / third-party / content-model / acpt Last commit date
class-avcf-abilities-acpt.php 1 month ago class-avcf-acpt-detector.php 1 month ago class-avcf-acpt-helpers.php 1 month ago
class-avcf-abilities-acpt.php
411 lines
1 <?php
2 /**
3 * ACPT (Advanced Custom Post Types) — MCP abilities (schema/authoring layer).
4 *
5 * Structure layer only; field VALUES are covered by the generic metadata
6 * abilities. Built on ACPT's Repository + Model ORM.
7 *
8 * Confidence (untested against live ACPT here):
9 * SOLID — reads (list/get) for post types, taxonomies, option pages, field
10 * groups; and create/edit/delete for post types / taxonomies /
11 * option pages (Model::hydrateFromArray + Repository::save).
12 * FLAGGED — field-group (meta group) create/edit nests boxes & fields; the
13 * MetaGroupModel shape is intricate — best-effort, validate live.
14 *
15 * @package atarim-visual-collaboration
16 */
17
18 if ( ! defined('ABSPATH') ) {
19 exit;
20 }
21
22 class AVCF_Abilities_ACPT extends AVCF_Abilities_Base {
23
24 /** @var AVCF_ACPT_Detector */
25 private $detector;
26
27 public function __construct() {
28 $this->detector = new AVCF_ACPT_Detector();
29 }
30
31 public function register() {
32 if ( ! $this->detector->avcf_acpt_is_available() || ! $this->detector->avcf_acpt_has_repositories() ) {
33 return;
34 }
35 $this->register_check_setup();
36 $this->register_field_type_schema();
37
38 $this->register_model( [
39 'prefix' => 'post-type', 'list' => 'post-types', 'label' => 'Post Type', 'plural' => 'post types',
40 'repo' => '\ACPT\Core\Repository\CustomPostTypeRepository',
41 'model' => '\ACPT\Core\Models\CustomPostType\CustomPostTypeModel',
42 'create_props' => [
43 'name' => [ 'type' => 'string', 'description' => 'Post type key (slug).' ],
44 'singular' => [ 'type' => 'string' ], 'plural' => [ 'type' => 'string' ],
45 'icon' => [ 'type' => 'string' ],
46 'supports' => [ 'type' => 'array' ], 'labels' => [ 'type' => 'object' ], 'settings' => [ 'type' => 'object' ],
47 ],
48 'create_required' => [ 'name', 'singular', 'plural' ],
49 'hydrate' => function( $input, $uuid ) {
50 return [
51 'id' => $uuid, 'name' => (string) $input['name'],
52 'singular' => isset( $input['singular'] ) ? (string) $input['singular'] : (string) $input['name'],
53 'plural' => isset( $input['plural'] ) ? (string) $input['plural'] : (string) $input['name'],
54 'icon' => isset( $input['icon'] ) ? (string) $input['icon'] : 'dashicons-admin-post',
55 'native' => false,
56 'supports' => isset( $input['supports'] ) ? (array) $input['supports'] : [ 'title', 'editor' ],
57 'labels' => isset( $input['labels'] ) ? (array) $input['labels'] : [],
58 'settings' => isset( $input['settings'] ) ? (array) $input['settings'] : [],
59 ];
60 },
61 'delete' => function( $repo, $name, $input ) { return $repo::delete( $name, ! empty( $input['delete_content'] ) ); },
62 'delete_props' => [ 'delete_content' => [ 'type' => 'boolean', 'default' => false, 'description' => 'Also delete posts of this type.' ] ],
63 ] );
64
65 $this->register_model( [
66 'prefix' => 'taxonomy', 'list' => 'taxonomies', 'label' => 'Taxonomy', 'plural' => 'taxonomies',
67 'repo' => '\ACPT\Core\Repository\TaxonomyRepository',
68 'model' => '\ACPT\Core\Models\Taxonomy\TaxonomyModel',
69 'create_props' => [
70 'slug' => [ 'type' => 'string', 'description' => 'Taxonomy key (slug).' ],
71 'singular' => [ 'type' => 'string' ], 'plural' => [ 'type' => 'string' ],
72 'post_types'=> [ 'type' => 'array', 'description' => 'Post type keys to attach to.' ],
73 'labels' => [ 'type' => 'object' ], 'settings' => [ 'type' => 'object' ],
74 ],
75 'create_required' => [ 'slug', 'singular', 'plural' ],
76 'hydrate' => function( $input, $uuid ) {
77 return [
78 'id' => $uuid, 'slug' => (string) $input['slug'],
79 'singular' => isset( $input['singular'] ) ? (string) $input['singular'] : (string) $input['slug'],
80 'plural' => isset( $input['plural'] ) ? (string) $input['plural'] : (string) $input['slug'],
81 'native' => false,
82 'postTypes'=> isset( $input['post_types'] ) ? (array) $input['post_types'] : [],
83 'labels' => isset( $input['labels'] ) ? (array) $input['labels'] : [],
84 'settings' => isset( $input['settings'] ) ? (array) $input['settings'] : [],
85 ];
86 },
87 'delete' => function( $repo, $name, $input ) { return $repo::delete( $name ); },
88 'name_key' => 'slug',
89 ] );
90
91 $this->register_model( [
92 'prefix' => 'option-page', 'list' => 'option-pages', 'label' => 'Option Page', 'plural' => 'option pages',
93 'repo' => '\ACPT\Core\Repository\OptionPageRepository',
94 'model' => '\ACPT\Core\Models\OptionPage\OptionPageModel',
95 'create_props' => [
96 'name' => [ 'type' => 'string', 'description' => 'Option page name.' ],
97 'menu_slug' => [ 'type' => 'string' ], 'menu_title' => [ 'type' => 'string' ],
98 'settings' => [ 'type' => 'object' ],
99 ],
100 'create_required' => [ 'name' ],
101 'hydrate' => function( $input, $uuid ) {
102 return [
103 'id' => $uuid, 'name' => (string) $input['name'],
104 'menuSlug' => isset( $input['menu_slug'] ) ? (string) $input['menu_slug'] : sanitize_title( (string) $input['name'] ),
105 'menuTitle' => isset( $input['menu_title'] ) ? (string) $input['menu_title'] : (string) $input['name'],
106 'settings' => isset( $input['settings'] ) ? (array) $input['settings'] : [],
107 ];
108 },
109 'delete' => function( $repo, $name, $input ) { return $repo::delete( $name ); },
110 ] );
111
112 $this->register_field_groups();
113 }
114
115 /* ------------------------------ shared ----------------------------- */
116
117 private function ro_meta() {
118 return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => true, 'destructive' => false, 'idempotent' => true ] ];
119 }
120 private function write_meta( $destructive = false ) {
121 return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => false, 'destructive' => (bool) $destructive, 'idempotent' => false ] ];
122 }
123 private function std_out() {
124 return [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ];
125 }
126
127 /* --------------------------- check-setup --------------------------- */
128
129 private function register_check_setup() {
130 $d = $this->detector;
131 wp_register_ability( 'atarim/acpt-check-setup', [
132 'label' => 'Check ACPT Setup', 'category' => 'atarim',
133 'description' => 'Call first. Reports whether ACPT is active and its version.',
134 'input_schema' => [ 'type' => 'object', 'properties' => [], 'additionalProperties' => false ],
135 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'active' => [ 'type' => 'boolean' ], 'version' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'active', 'message' ] ],
136 'execute_callback' => function( $input = [] ) use ( $d ) {
137 return [ 'success' => true, 'active' => $d->avcf_acpt_is_available(), 'version' => $d->avcf_acpt_version(), 'message' => 'OK.' ];
138 },
139 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
140 'meta' => $this->ro_meta(),
141 ] );
142 }
143
144 private function register_field_type_schema() {
145 wp_register_ability( 'atarim/acpt-get-field-type-schema', [
146 'label' => 'Get ACPT Field Type Schema', 'category' => 'atarim',
147 'description' => 'List the common ACPT field types to use when authoring field groups.',
148 'input_schema' => [ 'type' => 'object', 'properties' => [], 'additionalProperties' => false ],
149 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'field_types' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
150 'execute_callback' => function( $input = [] ) {
151 $types = [ 'text', 'textarea', 'number', 'email', 'url', 'phone', 'password', 'date', 'date_time', 'time', 'select', 'select_multi', 'radio', 'checkbox', 'toggle', 'range', 'image', 'gallery', 'file', 'video', 'wysiwyg', 'editor', 'color', 'post_object', 'page_select', 'taxonomy', 'user', 'currency', 'repeater', 'flexible', 'heading', 'html' ];
152 return [ 'success' => true, 'field_types' => array_map( function( $t ) { return [ 'type' => $t ]; }, $types ), 'message' => sprintf( '%d field types.', count( $types ) ) ];
153 },
154 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
155 'meta' => $this->ro_meta(),
156 ] );
157 }
158
159 /* ------------------- generic Repository+Model CRUD ----------------- */
160
161 private function register_model( $cfg ) {
162 $self = $this; $std = $this->std_out();
163 $repo = $cfg['repo'];
164 $model = $cfg['model'];
165 $prefix = $cfg['prefix'];
166 $label = $cfg['label'];
167 $plural = $cfg['plural'];
168 $name_key = isset( $cfg['name_key'] ) ? $cfg['name_key'] : 'name';
169 $list_slug = isset( $cfg['list'] ) ? $cfg['list'] : $prefix . 's';
170 $hydrate = $cfg['hydrate'];
171 $delete_cb = $cfg['delete'];
172
173 $guard = function() use ( $repo, $label ) {
174 if ( ! class_exists( $repo ) ) { return [ 'success' => false, 'message' => sprintf( 'ACPT %s repository is unavailable.', $label ) ]; }
175 return null;
176 };
177 $find_by_name = function( $name ) use ( $repo ) {
178 $all = AVCF_ACPT_Helpers::api_result( function() use ( $repo ) { return $repo::get(); } );
179 if ( is_wp_error( $all ) ) { return $all; }
180 foreach ( (array) $all as $m ) {
181 if ( is_object( $m ) && method_exists( $m, 'getName' ) && (string) $m->getName() === (string) $name ) { return $m; }
182 }
183 return null;
184 };
185
186 wp_register_ability( 'atarim/acpt-list-' . $list_slug, [
187 'label' => 'List ACPT ' . $label . 's', 'category' => 'atarim',
188 'description' => sprintf( 'List ACPT %s.', $plural ),
189 'input_schema' => [ 'type' => 'object', 'properties' => [], 'additionalProperties' => false ],
190 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'items' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
191 'execute_callback' => function( $input = [] ) use ( $guard, $repo ) {
192 $g = $guard(); if ( $g ) { return $g; }
193 $all = AVCF_ACPT_Helpers::api_result( function() use ( $repo ) { return $repo::get(); } );
194 if ( is_wp_error( $all ) ) { return [ 'success' => false, 'message' => $all->get_error_message() ]; }
195 $out = [];
196 foreach ( (array) $all as $m ) { $out[] = AVCF_ACPT_Helpers::shape( $m ); }
197 return [ 'success' => true, 'items' => $out, 'message' => sprintf( '%d item(s).', count( $out ) ) ];
198 },
199 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
200 'meta' => $this->ro_meta(),
201 ] );
202
203 wp_register_ability( 'atarim/acpt-get-' . $prefix, [
204 'label' => 'Get ACPT ' . $label, 'category' => 'atarim',
205 'description' => sprintf( 'Get one ACPT %s by %s.', $prefix, $name_key ),
206 'input_schema' => [ 'type' => 'object', 'properties' => [ $name_key => [ 'type' => 'string' ] ], 'required' => [ $name_key ], 'additionalProperties' => false ],
207 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'item' => [ 'type' => 'object' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
208 'execute_callback' => function( $input = [] ) use ( $guard, $find_by_name, $name_key, $label ) {
209 $g = $guard(); if ( $g ) { return $g; }
210 $m = $find_by_name( (string) $input[ $name_key ] );
211 if ( is_wp_error( $m ) ) { return [ 'success' => false, 'message' => $m->get_error_message() ]; }
212 if ( ! $m ) { return [ 'success' => false, 'message' => sprintf( '%s "%s" not found.', $label, $input[ $name_key ] ) ]; }
213 return [ 'success' => true, 'item' => AVCF_ACPT_Helpers::shape( $m ), 'message' => 'OK.' ];
214 },
215 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
216 'meta' => $this->ro_meta(),
217 ] );
218
219 wp_register_ability( 'atarim/acpt-create-' . $prefix, [
220 'label' => 'Create ACPT ' . $label, 'category' => 'atarim',
221 'description' => sprintf( 'Create an ACPT %s.', $prefix ),
222 'input_schema' => [ 'type' => 'object', 'properties' => $cfg['create_props'], 'required' => $cfg['create_required'], 'additionalProperties' => false ],
223 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'id' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
224 'execute_callback' => function( $input = [] ) use ( $guard, $repo, $model, $hydrate ) {
225 $g = $guard(); if ( $g ) { return $g; }
226 $uuid = AVCF_ACPT_Helpers::uuid();
227 $arr = call_user_func( $hydrate, $input, $uuid );
228 $r = AVCF_ACPT_Helpers::api_result( function() use ( $model, $repo, $arr ) {
229 $obj = $model::hydrateFromArray( $arr );
230 $repo::save( $obj );
231 return true;
232 } );
233 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => $r->get_error_message() ]; }
234 return [ 'success' => true, 'id' => $uuid, 'message' => 'Created.' ];
235 },
236 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
237 'meta' => $this->write_meta( false ),
238 ] );
239
240 wp_register_ability( 'atarim/acpt-edit-' . $prefix, [
241 'label' => 'Edit ACPT ' . $label, 'category' => 'atarim',
242 'description' => sprintf( 'Update an ACPT %s by %s. Supplied keys are merged into the existing definition.', $prefix, $name_key ),
243 'input_schema' => [ 'type' => 'object', 'properties' => array_merge( [ $name_key => [ 'type' => 'string' ] ], $cfg['create_props'] ), 'required' => [ $name_key ], 'additionalProperties' => false ],
244 'output_schema'=> $std,
245 'execute_callback' => function( $input = [] ) use ( $guard, $find_by_name, $repo, $model, $name_key, $label, $hydrate ) {
246 $g = $guard(); if ( $g ) { return $g; }
247 $existing = $find_by_name( (string) $input[ $name_key ] );
248 if ( is_wp_error( $existing ) ) { return [ 'success' => false, 'message' => $existing->get_error_message() ]; }
249 if ( ! $existing ) { return [ 'success' => false, 'message' => sprintf( '%s "%s" not found.', $label, $input[ $name_key ] ) ]; }
250 $base = AVCF_ACPT_Helpers::shape( $existing );
251 $uuid = isset( $base['id'] ) ? $base['id'] : AVCF_ACPT_Helpers::uuid();
252 // Re-hydrate from input, preserving the existing id.
253 $arr = call_user_func( $hydrate, array_merge( $base, $input ), $uuid );
254 $arr['id'] = $uuid;
255 $r = AVCF_ACPT_Helpers::api_result( function() use ( $model, $repo, $arr ) {
256 $obj = $model::hydrateFromArray( $arr );
257 $repo::save( $obj );
258 return true;
259 } );
260 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => $r->get_error_message() ]; }
261 return [ 'success' => true, 'message' => 'Updated.' ];
262 },
263 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
264 'meta' => $this->write_meta( false ),
265 ] );
266
267 $delete_props = isset( $cfg['delete_props'] ) ? $cfg['delete_props'] : [];
268 wp_register_ability( 'atarim/acpt-delete-' . $prefix, [
269 'label' => 'Delete ACPT ' . $label, 'category' => 'atarim',
270 'description' => sprintf( 'Delete an ACPT %s by %s. Dry run unless confirm:true.', $prefix, $name_key ),
271 'input_schema' => [ 'type' => 'object', 'properties' => array_merge( [ $name_key => [ 'type' => 'string' ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], $delete_props ), 'required' => [ $name_key ], 'additionalProperties' => false ],
272 'output_schema'=> $std,
273 'execute_callback' => function( $input = [] ) use ( $guard, $repo, $name_key, $label, $delete_cb ) {
274 $g = $guard(); if ( $g ) { return $g; }
275 $name = (string) $input[ $name_key ];
276 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'deleted' => false, 'message' => sprintf( 'Dry run: would delete %s "%s". Re-call with confirm:true.', $label, $name ) ]; }
277 $r = AVCF_ACPT_Helpers::api_result( function() use ( $delete_cb, $repo, $name, $input ) { return call_user_func( $delete_cb, $repo, $name, $input ); } );
278 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => $r->get_error_message() ]; }
279 return [ 'success' => true, 'deleted' => true, 'message' => sprintf( '%s "%s" deleted.', $label, $name ) ];
280 },
281 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
282 'meta' => $this->write_meta( true ),
283 ] );
284 }
285
286 /* --------------------------- field groups -------------------------- */
287
288 private function register_field_groups() {
289 $self = $this; $std = $this->std_out();
290 $repo = '\ACPT\Core\Repository\MetaRepository';
291 $guard = function() use ( $repo ) {
292 if ( ! class_exists( $repo ) ) { return [ 'success' => false, 'message' => 'ACPT MetaRepository is unavailable.' ]; }
293 return null;
294 };
295
296 wp_register_ability( 'atarim/acpt-list-field-groups', [
297 'label' => 'List ACPT Field Groups', 'category' => 'atarim',
298 'description' => 'List ACPT meta (field) groups (id, name, label).',
299 'input_schema' => [ 'type' => 'object', 'properties' => [], 'additionalProperties' => false ],
300 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'field_groups' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
301 'execute_callback' => function( $input = [] ) use ( $guard, $repo ) {
302 $g = $guard(); if ( $g ) { return $g; }
303 $r = AVCF_ACPT_Helpers::api_result( function() use ( $repo ) { return $repo::get(); } );
304 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => $r->get_error_message() ]; }
305 $out = [];
306 foreach ( (array) $r as $g2 ) { $out[] = AVCF_ACPT_Helpers::shape( $g2 ); }
307 return [ 'success' => true, 'field_groups' => $out, 'message' => sprintf( '%d group(s).', count( $out ) ) ];
308 },
309 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
310 'meta' => $this->ro_meta(),
311 ] );
312
313 wp_register_ability( 'atarim/acpt-get-field-group-schema', [
314 'label' => 'Get ACPT Field Group Schema', 'category' => 'atarim',
315 'description' => 'Get one ACPT meta group (with its boxes/fields) by name.',
316 'input_schema' => [ 'type' => 'object', 'properties' => [ 'name' => [ 'type' => 'string' ] ], 'required' => [ 'name' ], 'additionalProperties' => false ],
317 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'field_group' => [ 'type' => 'object' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
318 'execute_callback' => function( $input = [] ) use ( $guard, $repo ) {
319 $g = $guard(); if ( $g ) { return $g; }
320 $name = (string) $input['name'];
321 $r = AVCF_ACPT_Helpers::api_result( function() use ( $repo ) { return $repo::get(); } );
322 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => $r->get_error_message() ]; }
323 foreach ( (array) $r as $grp ) {
324 if ( is_object( $grp ) && method_exists( $grp, 'getName' ) && (string) $grp->getName() === $name ) {
325 return [ 'success' => true, 'field_group' => AVCF_ACPT_Helpers::shape( $grp ), 'message' => 'OK.' ];
326 }
327 }
328 return [ 'success' => false, 'message' => sprintf( 'Field group "%s" not found.', $name ) ];
329 },
330 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
331 'meta' => $this->ro_meta(),
332 ] );
333
334 $note = ' BEST-EFFORT: ACPT meta groups nest boxes and fields; the MetaGroupModel shape is intricate and unverified — validate on a live ACPT install.';
335 $model = '\ACPT\Core\Models\Meta\MetaGroupModel';
336
337 wp_register_ability( 'atarim/acpt-create-field-group', [
338 'label' => 'Create ACPT Field Group', 'category' => 'atarim',
339 'description' => 'Create an ACPT meta (field) group.' . $note . ' Provide name, label, and optionally boxes (array of box defs with fields).',
340 'input_schema' => [ 'type' => 'object', 'properties' => [ 'name' => [ 'type' => 'string' ], 'label' => [ 'type' => 'string' ], 'boxes' => [ 'type' => 'array' ] ], 'required' => [ 'name' ], 'additionalProperties' => false ],
341 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'id' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
342 'execute_callback' => function( $input = [] ) use ( $guard, $repo, $model ) {
343 $g = $guard(); if ( $g ) { return $g; }
344 if ( ! class_exists( $model ) || ! method_exists( $model, 'hydrateFromArray' ) ) {
345 return [ 'success' => false, 'message' => 'ACPT MetaGroupModel is not available on this build; field-group authoring needs live-ACPT validation.' ];
346 }
347 $uuid = AVCF_ACPT_Helpers::uuid();
348 $arr = [ 'id' => $uuid, 'name' => (string) $input['name'], 'label' => isset( $input['label'] ) ? (string) $input['label'] : (string) $input['name'], 'boxes' => isset( $input['boxes'] ) ? (array) $input['boxes'] : [] ];
349 $save = method_exists( $repo, 'saveMetaGroup' ) ? 'saveMetaGroup' : 'save';
350 $r = AVCF_ACPT_Helpers::api_result( function() use ( $model, $repo, $arr, $save ) {
351 $obj = $model::hydrateFromArray( $arr );
352 call_user_func( [ $repo, $save ], $obj );
353 return true;
354 } );
355 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => $r->get_error_message() ]; }
356 return [ 'success' => true, 'id' => $uuid, 'message' => 'Field group created (best-effort — verify in ACPT).' ];
357 },
358 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
359 'meta' => $this->write_meta( false ),
360 ] );
361
362 wp_register_ability( 'atarim/acpt-edit-field-group', [
363 'label' => 'Edit ACPT Field Group', 'category' => 'atarim',
364 'description' => 'Update an ACPT meta group by name.' . $note,
365 'input_schema' => [ 'type' => 'object', 'properties' => [ 'name' => [ 'type' => 'string' ], 'label' => [ 'type' => 'string' ], 'boxes' => [ 'type' => 'array' ] ], 'required' => [ 'name' ], 'additionalProperties' => false ],
366 'output_schema'=> $std,
367 'execute_callback' => function( $input = [] ) use ( $guard, $repo, $model ) {
368 $g = $guard(); if ( $g ) { return $g; }
369 $name = (string) $input['name'];
370 $all = AVCF_ACPT_Helpers::api_result( function() use ( $repo ) { return $repo::get(); } );
371 if ( is_wp_error( $all ) ) { return [ 'success' => false, 'message' => $all->get_error_message() ]; }
372 $existing = null;
373 foreach ( (array) $all as $grp ) { if ( is_object( $grp ) && method_exists( $grp, 'getName' ) && (string) $grp->getName() === $name ) { $existing = $grp; break; } }
374 if ( ! $existing ) { return [ 'success' => false, 'message' => sprintf( 'Field group "%s" not found.', $name ) ]; }
375 $base = AVCF_ACPT_Helpers::shape( $existing );
376 $arr = array_merge( $base, [ 'name' => $name ] );
377 if ( isset( $input['label'] ) ) { $arr['label'] = (string) $input['label']; }
378 if ( isset( $input['boxes'] ) ) { $arr['boxes'] = (array) $input['boxes']; }
379 $save = method_exists( $repo, 'saveMetaGroup' ) ? 'saveMetaGroup' : 'save';
380 $r = AVCF_ACPT_Helpers::api_result( function() use ( $model, $repo, $arr, $save ) {
381 $obj = $model::hydrateFromArray( $arr );
382 call_user_func( [ $repo, $save ], $obj );
383 return true;
384 } );
385 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => $r->get_error_message() ]; }
386 return [ 'success' => true, 'message' => 'Field group updated (best-effort).' ];
387 },
388 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
389 'meta' => $this->write_meta( false ),
390 ] );
391
392 wp_register_ability( 'atarim/acpt-delete-field-group', [
393 'label' => 'Delete ACPT Field Group', 'category' => 'atarim',
394 'description' => 'Delete an ACPT meta group by name. Dry run unless confirm:true.',
395 'input_schema' => [ 'type' => 'object', 'properties' => [ 'name' => [ 'type' => 'string' ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'name' ], 'additionalProperties' => false ],
396 'output_schema'=> $std,
397 'execute_callback' => function( $input = [] ) use ( $guard, $repo ) {
398 $g = $guard(); if ( $g ) { return $g; }
399 $name = (string) $input['name'];
400 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'deleted' => false, 'message' => sprintf( 'Dry run: would delete field group "%s". Re-call with confirm:true.', $name ) ]; }
401 if ( ! method_exists( $repo, 'deleteMetaGroup' ) ) { return [ 'success' => false, 'message' => 'MetaRepository::deleteMetaGroup() not available on this build.' ]; }
402 $r = AVCF_ACPT_Helpers::api_result( function() use ( $repo, $name ) { return $repo::deleteMetaGroup( $name ); } );
403 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => $r->get_error_message() ]; }
404 return [ 'success' => true, 'deleted' => true, 'message' => 'Field group deleted.' ];
405 },
406 'permission_callback' => function() { return current_user_can( 'manage_options' ); },
407 'meta' => $this->write_meta( true ),
408 ] );
409 }
410 }
411