class-avcf-abilities-acpt.php
2 days ago
class-avcf-acpt-detector.php
2 days ago
class-avcf-acpt-helpers.php
2 days 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 |