atarim-visual-collaboration
/
third-party
/
forms
/
fluent-forms
/
class-avcf-abilities-fluent-pro.php
class-avcf-abilities-fluent-pro.php
2 days ago
class-avcf-abilities-fluent.php
2 days ago
class-avcf-fluent-detector.php
2 days ago
class-avcf-fluent-helpers.php
2 days ago
class-avcf-abilities-fluent-pro.php
412 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Fluent Forms — power MCP abilities (standalone cluster, -pro file). |
| 4 | * |
| 5 | * Fluent-specific depth: form CRUD, submission status/favourite/delete workflow, |
| 6 | * a generic form_meta read/write (Fluent keeps notifications, confirmations and |
| 7 | * integration feeds as JSON rows in fluentform_form_meta), typed notification |
| 8 | * helpers, confirmation read, and an integration-feed listing. Writers use |
| 9 | * Fluent's capabilities with a manage_options fallback; deletes are |
| 10 | * confirm-gated. create-form is experimental (raw insert of Fluent's JSON |
| 11 | * schema). Direct $wpdb; not runtime-tested here. |
| 12 | * |
| 13 | * @package atarim-visual-collaboration |
| 14 | */ |
| 15 | |
| 16 | if ( ! defined('ABSPATH') ) { |
| 17 | exit; |
| 18 | } |
| 19 | |
| 20 | class AVCF_Abilities_Fluent_Pro extends AVCF_Abilities_Base { |
| 21 | |
| 22 | /** @var AVCF_Fluent_Detector */ |
| 23 | private $detector; |
| 24 | |
| 25 | public function __construct() { |
| 26 | $this->detector = new AVCF_Fluent_Detector(); |
| 27 | } |
| 28 | |
| 29 | public function register() { |
| 30 | if ( ! $this->detector->avcf_fluent_is_available() ) { |
| 31 | return; |
| 32 | } |
| 33 | $this->register_create_form(); |
| 34 | $this->register_update_form(); |
| 35 | $this->register_delete_form(); |
| 36 | $this->register_delete_entry(); |
| 37 | $this->register_set_entry_status(); |
| 38 | $this->register_favorite_entry(); |
| 39 | $this->register_list_form_meta(); |
| 40 | $this->register_get_form_meta(); |
| 41 | $this->register_save_form_meta(); |
| 42 | $this->register_list_notifications(); |
| 43 | $this->register_save_notification(); |
| 44 | $this->register_get_confirmation(); |
| 45 | $this->register_list_integration_feeds(); |
| 46 | } |
| 47 | |
| 48 | /* ------------------------------ shared ----------------------------- */ |
| 49 | |
| 50 | private function ro_meta() { return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => true, 'destructive' => false, 'idempotent' => true ] ]; } |
| 51 | private function write_meta( $d = false ) { return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => false, 'destructive' => (bool) $d, 'idempotent' => false ] ]; } |
| 52 | private function std() { return [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ]; } |
| 53 | |
| 54 | public function can_edit_forms() { return current_user_can( 'fluentform_forms_manager' ) || current_user_can( 'manage_options' ); } |
| 55 | public function can_edit_entries() { return current_user_can( 'fluentform_entries_viewer' ) || current_user_can( 'manage_options' ); } |
| 56 | public function can_settings() { return current_user_can( 'fluentform_settings_manager' ) || current_user_can( 'fluentform_forms_manager' ) || current_user_can( 'manage_options' ); } |
| 57 | |
| 58 | public function form_exists( $form_id ) { |
| 59 | global $wpdb; $t = AVCF_Fluent_Helpers::forms_table(); |
| 60 | return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$t}` WHERE id = %d", (int) $form_id ) ) > 0; |
| 61 | } |
| 62 | /** Decoded JSON for a meta_key, or null. */ |
| 63 | public function get_meta( $form_id, $key ) { |
| 64 | global $wpdb; $t = AVCF_Fluent_Helpers::meta_table(); |
| 65 | $val = $wpdb->get_var( $wpdb->prepare( "SELECT value FROM `{$t}` WHERE form_id = %d AND meta_key = %s LIMIT 1", (int) $form_id, (string) $key ) ); |
| 66 | if ( $val === null ) { return null; } |
| 67 | $decoded = json_decode( $val, true ); |
| 68 | return $decoded === null ? $val : $decoded; |
| 69 | } |
| 70 | public function set_meta( $form_id, $key, $value ) { |
| 71 | global $wpdb; $t = AVCF_Fluent_Helpers::meta_table(); |
| 72 | $json = is_string( $value ) ? $value : wp_json_encode( $value ); |
| 73 | $id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM `{$t}` WHERE form_id = %d AND meta_key = %s LIMIT 1", (int) $form_id, (string) $key ) ); |
| 74 | if ( $id ) { return $wpdb->update( $t, [ 'value' => $json ], [ 'id' => (int) $id ], [ '%s' ], [ '%d' ] ); } |
| 75 | return $wpdb->insert( $t, [ 'form_id' => (int) $form_id, 'meta_key' => (string) $key, 'value' => $json ] ); |
| 76 | } |
| 77 | |
| 78 | /* ----------------------------- create-form ------------------------- */ |
| 79 | |
| 80 | private function register_create_form() { |
| 81 | $self = $this; |
| 82 | wp_register_ability( 'atarim/fluent-create-form', [ |
| 83 | 'label' => 'Create Fluent Form', 'category' => 'atarim', |
| 84 | 'description' => 'Create a new Fluent form. EXPERIMENTAL: builds a minimal valid form_fields JSON from a simple fields array (each: { type (e.g. input_text, input_email, textarea, select), label, name?, required?, options?[] }) plus a submit button. Returns the new form_id. Complex field types should be refined in the Fluent editor afterwards.', |
| 85 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'title' => [ 'type' => 'string', 'minLength' => 1 ], 'fields' => [ 'type' => 'array', 'items' => [ 'type' => 'object' ] ], 'status' => [ 'type' => 'string', 'enum' => [ 'published', 'unpublished' ], 'default' => 'published' ] ], 'required' => [ 'title' ], 'additionalProperties' => false ], |
| 86 | 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'form_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ], |
| 87 | 'execute_callback' => function( $input = [] ) use ( $self ) { |
| 88 | global $wpdb; |
| 89 | $title = sanitize_text_field( $input['title'] ); |
| 90 | if ( $title === '' ) { return [ 'success' => false, 'message' => 'A title is required.' ]; } |
| 91 | $fields = []; |
| 92 | $defs = isset( $input['fields'] ) && is_array( $input['fields'] ) ? $input['fields'] : []; |
| 93 | $i = 1; |
| 94 | foreach ( $defs as $f ) { |
| 95 | if ( ! is_array( $f ) ) { continue; } |
| 96 | $element = isset( $f['type'] ) ? (string) $f['type'] : 'input_text'; |
| 97 | $name = isset( $f['name'] ) && $f['name'] !== '' ? sanitize_key( $f['name'] ) : ( 'field_' . $i ); |
| 98 | $label = isset( $f['label'] ) ? (string) $f['label'] : ( 'Field ' . $i ); |
| 99 | $settings = [ 'label' => $label, 'validation_rules' => [ 'required' => [ 'value' => ! empty( $f['required'] ), 'message' => 'This field is required.' ] ] ]; |
| 100 | if ( isset( $f['options'] ) && is_array( $f['options'] ) ) { |
| 101 | $settings['advanced_options'] = []; |
| 102 | foreach ( $f['options'] as $opt ) { $txt = is_array( $opt ) ? ( isset( $opt['label'] ) ? $opt['label'] : '' ) : (string) $opt; $settings['advanced_options'][] = [ 'label' => (string) $txt, 'value' => sanitize_title( (string) $txt ) ]; } |
| 103 | } |
| 104 | $fields[] = [ 'index' => $i - 1, 'element' => $element, 'attributes' => [ 'type' => 'text', 'name' => $name ], 'settings' => $settings, 'editor_options' => [ 'title' => $label ] ]; |
| 105 | $i++; |
| 106 | } |
| 107 | $form_fields = wp_json_encode( [ 'fields' => $fields, 'submitButton' => [ 'uniqElKey' => 'el_' . time(), 'element' => 'button', 'attributes' => [ 'type' => 'submit', 'class' => '' ], 'settings' => [ 'button_style' => 'default', 'align' => 'left', 'button_size' => 'md', 'color' => '#ffffff', 'background_color' => '#409EFF', 'button_ui' => [ 'type' => 'default', 'text' => 'Submit' ] ] ] ] ); |
| 108 | $now = current_time( 'mysql' ); |
| 109 | $ok = $wpdb->insert( AVCF_Fluent_Helpers::forms_table(), [ 'title' => $title, 'form_fields' => $form_fields, 'status' => isset( $input['status'] ) ? (string) $input['status'] : 'published', 'has_payment' => 0, 'type' => 'form', 'created_by' => get_current_user_id(), 'created_at' => $now, 'updated_at' => $now ] ); |
| 110 | if ( ! $ok ) { return [ 'success' => false, 'message' => 'Create failed (schema mismatch possible).' ]; } |
| 111 | return [ 'success' => true, 'form_id' => (int) $wpdb->insert_id, 'message' => sprintf( 'Form %d created (experimental — verify in the Fluent editor).', (int) $wpdb->insert_id ) ]; |
| 112 | }, |
| 113 | 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); }, |
| 114 | 'meta' => $this->write_meta( false ), |
| 115 | ] ); |
| 116 | } |
| 117 | |
| 118 | /* ----------------------------- update-form ------------------------- */ |
| 119 | |
| 120 | private function register_update_form() { |
| 121 | $self = $this; |
| 122 | wp_register_ability( 'atarim/fluent-update-form', [ |
| 123 | 'label' => 'Update Fluent Form', 'category' => 'atarim', |
| 124 | 'description' => 'Update a form\'s title and/or status, and/or replace its form_fields with a full structure object (must contain "fields"). Only provided keys change.', |
| 125 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'title' => [ 'type' => 'string' ], 'status' => [ 'type' => 'string', 'enum' => [ 'published', 'unpublished' ] ], 'form_fields' => [ 'type' => 'object' ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ], |
| 126 | 'output_schema'=> $this->std(), |
| 127 | 'execute_callback' => function( $input = [] ) use ( $self ) { |
| 128 | global $wpdb; |
| 129 | $form_id = (int) $input['form_id']; |
| 130 | if ( ! $self->form_exists( $form_id ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; } |
| 131 | $data = [ 'updated_at' => current_time( 'mysql' ) ]; $fmt = [ '%s' ]; |
| 132 | if ( isset( $input['title'] ) ) { $data['title'] = sanitize_text_field( $input['title'] ); $fmt[] = '%s'; } |
| 133 | if ( isset( $input['status'] ) ) { $data['status'] = (string) $input['status']; $fmt[] = '%s'; } |
| 134 | if ( isset( $input['form_fields'] ) && is_array( $input['form_fields'] ) ) { |
| 135 | if ( ! isset( $input['form_fields']['fields'] ) ) { return [ 'success' => false, 'message' => 'form_fields must contain a "fields" array.' ]; } |
| 136 | $data['form_fields'] = wp_json_encode( $input['form_fields'] ); $fmt[] = '%s'; |
| 137 | } |
| 138 | $r = $wpdb->update( AVCF_Fluent_Helpers::forms_table(), $data, [ 'id' => $form_id ], $fmt, [ '%d' ] ); |
| 139 | if ( $r === false ) { return [ 'success' => false, 'message' => 'Update failed.' ]; } |
| 140 | return [ 'success' => true, 'message' => 'Form updated.' ]; |
| 141 | }, |
| 142 | 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); }, |
| 143 | 'meta' => $this->write_meta( false ), |
| 144 | ] ); |
| 145 | } |
| 146 | |
| 147 | /* ----------------------------- delete-form ------------------------- */ |
| 148 | |
| 149 | private function register_delete_form() { |
| 150 | $self = $this; |
| 151 | wp_register_ability( 'atarim/fluent-delete-form', [ |
| 152 | 'label' => 'Delete Fluent Form', 'category' => 'atarim', |
| 153 | 'description' => 'Permanently delete a form, its form_meta, and all its submissions (+ submission_meta). DESTRUCTIVE — dry run unless confirm:true.', |
| 154 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ], |
| 155 | 'output_schema'=> $this->std(), |
| 156 | 'execute_callback' => function( $input = [] ) use ( $self ) { |
| 157 | global $wpdb; |
| 158 | $form_id = (int) $input['form_id']; |
| 159 | if ( ! $self->form_exists( $form_id ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; } |
| 160 | $sub_count = AVCF_Fluent_Helpers::count_entries( $form_id, true ); |
| 161 | if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => sprintf( 'Dry run: re-call with confirm:true to delete this form, its settings, and %d submission(s).', $sub_count ) ]; } |
| 162 | $subs = AVCF_Fluent_Helpers::subs_table(); |
| 163 | $sub_ids = $wpdb->get_col( $wpdb->prepare( "SELECT id FROM `{$subs}` WHERE form_id = %d", $form_id ) ); |
| 164 | if ( $sub_ids ) { |
| 165 | $smeta = $wpdb->prefix . 'fluentform_submission_meta'; |
| 166 | $in = implode( ',', array_map( 'intval', $sub_ids ) ); |
| 167 | $wpdb->query( "DELETE FROM `{$smeta}` WHERE response_id IN ({$in})" ); |
| 168 | } |
| 169 | $wpdb->delete( $subs, [ 'form_id' => $form_id ], [ '%d' ] ); |
| 170 | $wpdb->delete( AVCF_Fluent_Helpers::meta_table(), [ 'form_id' => $form_id ], [ '%d' ] ); |
| 171 | $wpdb->delete( AVCF_Fluent_Helpers::forms_table(), [ 'id' => $form_id ], [ '%d' ] ); |
| 172 | return [ 'success' => true, 'message' => sprintf( 'Form %d and %d submission(s) deleted.', $form_id, count( (array) $sub_ids ) ) ]; |
| 173 | }, |
| 174 | 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); }, |
| 175 | 'meta' => $this->write_meta( true ), |
| 176 | ] ); |
| 177 | } |
| 178 | |
| 179 | /* ----------------------------- delete-entry ------------------------ */ |
| 180 | |
| 181 | private function register_delete_entry() { |
| 182 | $self = $this; |
| 183 | wp_register_ability( 'atarim/fluent-delete-entry', [ |
| 184 | 'label' => 'Delete Fluent Entry', 'category' => 'atarim', |
| 185 | 'description' => 'Permanently delete a submission and its meta. DESTRUCTIVE — dry run unless confirm:true.', |
| 186 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'entry_id' ], 'additionalProperties' => false ], |
| 187 | 'output_schema'=> $this->std(), |
| 188 | 'execute_callback' => function( $input = [] ) { |
| 189 | global $wpdb; |
| 190 | $entry_id = (int) $input['entry_id']; |
| 191 | $subs = AVCF_Fluent_Helpers::subs_table(); |
| 192 | if ( (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$subs}` WHERE id = %d", $entry_id ) ) === 0 ) { return [ 'success' => false, 'message' => 'Entry not found.' ]; } |
| 193 | if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => 'Dry run: re-call with confirm:true to permanently delete this submission.' ]; } |
| 194 | $smeta = $wpdb->prefix . 'fluentform_submission_meta'; |
| 195 | $wpdb->delete( $smeta, [ 'response_id' => $entry_id ], [ '%d' ] ); |
| 196 | $wpdb->delete( $subs, [ 'id' => $entry_id ], [ '%d' ] ); |
| 197 | return [ 'success' => true, 'message' => sprintf( 'Entry %d deleted.', $entry_id ) ]; |
| 198 | }, |
| 199 | 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); }, |
| 200 | 'meta' => $this->write_meta( true ), |
| 201 | ] ); |
| 202 | } |
| 203 | |
| 204 | /* --------------------------- set-entry-status ---------------------- */ |
| 205 | |
| 206 | private function register_set_entry_status() { |
| 207 | $self = $this; |
| 208 | wp_register_ability( 'atarim/fluent-set-entry-status', [ |
| 209 | 'label' => 'Set Fluent Entry Status', 'category' => 'atarim', |
| 210 | 'description' => 'Set a submission\'s status: unread, read, spam, or trashed.', |
| 211 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'status' => [ 'type' => 'string', 'enum' => [ 'unread', 'read', 'spam', 'trashed' ] ] ], 'required' => [ 'entry_id', 'status' ], 'additionalProperties' => false ], |
| 212 | 'output_schema'=> $this->std(), |
| 213 | 'execute_callback' => function( $input = [] ) { |
| 214 | global $wpdb; |
| 215 | $r = $wpdb->update( AVCF_Fluent_Helpers::subs_table(), [ 'status' => (string) $input['status'] ], [ 'id' => (int) $input['entry_id'] ], [ '%s' ], [ '%d' ] ); |
| 216 | if ( $r === false ) { return [ 'success' => false, 'message' => 'Update failed.' ]; } |
| 217 | return [ 'success' => true, 'message' => sprintf( 'Entry status set to %s.', (string) $input['status'] ) ]; |
| 218 | }, |
| 219 | 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); }, |
| 220 | 'meta' => $this->write_meta( false ), |
| 221 | ] ); |
| 222 | } |
| 223 | |
| 224 | /* ---------------------------- favorite-entry ----------------------- */ |
| 225 | |
| 226 | private function register_favorite_entry() { |
| 227 | $self = $this; |
| 228 | wp_register_ability( 'atarim/fluent-favorite-entry', [ |
| 229 | 'label' => 'Favorite Fluent Entry', 'category' => 'atarim', |
| 230 | 'description' => 'Mark a submission as favourite (or clear with favorite:false).', |
| 231 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'favorite' => [ 'type' => 'boolean', 'default' => true ] ], 'required' => [ 'entry_id' ], 'additionalProperties' => false ], |
| 232 | 'output_schema'=> $this->std(), |
| 233 | 'execute_callback' => function( $input = [] ) { |
| 234 | global $wpdb; |
| 235 | $val = ! isset( $input['favorite'] ) || ! empty( $input['favorite'] ) ? 1 : 0; |
| 236 | $r = $wpdb->update( AVCF_Fluent_Helpers::subs_table(), [ 'is_favourite' => $val ], [ 'id' => (int) $input['entry_id'] ], [ '%d' ], [ '%d' ] ); |
| 237 | if ( $r === false ) { return [ 'success' => false, 'message' => 'Update failed.' ]; } |
| 238 | return [ 'success' => true, 'message' => $val ? 'Entry favourited.' : 'Entry unfavourited.' ]; |
| 239 | }, |
| 240 | 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); }, |
| 241 | 'meta' => $this->write_meta( false ), |
| 242 | ] ); |
| 243 | } |
| 244 | |
| 245 | /* ---------------------------- list-form-meta ----------------------- */ |
| 246 | |
| 247 | private function register_list_form_meta() { |
| 248 | $self = $this; |
| 249 | wp_register_ability( 'atarim/fluent-list-form-meta', [ |
| 250 | 'label' => 'List Fluent Form Meta Keys', 'category' => 'atarim', |
| 251 | 'description' => 'List the form_meta keys stored on a form (e.g. notifications, formSettings, *_feeds), with value sizes. Use fluent-get-form-meta to read one.', |
| 252 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ], |
| 253 | 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'meta_keys' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ], |
| 254 | 'execute_callback' => function( $input = [] ) { |
| 255 | global $wpdb; $t = AVCF_Fluent_Helpers::meta_table(); |
| 256 | $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id, meta_key, LENGTH(value) AS len FROM `{$t}` WHERE form_id = %d ORDER BY meta_key ASC", (int) $input['form_id'] ) ); |
| 257 | $out = []; |
| 258 | foreach ( (array) $rows as $r ) { $out[] = [ 'id' => (int) $r->id, 'meta_key' => (string) $r->meta_key, 'value_length' => (int) $r->len ]; } |
| 259 | return [ 'success' => true, 'meta_keys' => $out, 'message' => sprintf( '%d meta key(s).', count( $out ) ) ]; |
| 260 | }, |
| 261 | 'permission_callback' => function() use ( $self ) { return $self->can_settings(); }, |
| 262 | 'meta' => $this->ro_meta(), |
| 263 | ] ); |
| 264 | } |
| 265 | |
| 266 | /* ----------------------------- get-form-meta ----------------------- */ |
| 267 | |
| 268 | private function register_get_form_meta() { |
| 269 | $self = $this; |
| 270 | wp_register_ability( 'atarim/fluent-get-form-meta', [ |
| 271 | 'label' => 'Get Fluent Form Meta', 'category' => 'atarim', |
| 272 | 'description' => 'Read and JSON-decode a single form_meta value by meta_key (e.g. notifications, formSettings).', |
| 273 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'meta_key' => [ 'type' => 'string', 'minLength' => 1 ] ], 'required' => [ 'form_id', 'meta_key' ], 'additionalProperties' => false ], |
| 274 | 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'value' => [], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ], |
| 275 | 'execute_callback' => function( $input = [] ) use ( $self ) { |
| 276 | $val = $self->get_meta( (int) $input['form_id'], (string) $input['meta_key'] ); |
| 277 | if ( $val === null ) { return [ 'success' => false, 'message' => 'Meta key not found on this form.' ]; } |
| 278 | return [ 'success' => true, 'value' => $val, 'message' => 'OK.' ]; |
| 279 | }, |
| 280 | 'permission_callback' => function() use ( $self ) { return $self->can_settings(); }, |
| 281 | 'meta' => $this->ro_meta(), |
| 282 | ] ); |
| 283 | } |
| 284 | |
| 285 | /* ---------------------------- save-form-meta ----------------------- */ |
| 286 | |
| 287 | private function register_save_form_meta() { |
| 288 | $self = $this; |
| 289 | wp_register_ability( 'atarim/fluent-save-form-meta', [ |
| 290 | 'label' => 'Save Fluent Form Meta', 'category' => 'atarim', |
| 291 | 'description' => 'Create or update a form_meta key with a JSON value (object/array). Powerful generic settings writer — use deliberately. Returns nothing beyond success.', |
| 292 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'meta_key' => [ 'type' => 'string', 'minLength' => 1 ], 'value' => [] ], 'required' => [ 'form_id', 'meta_key', 'value' ], 'additionalProperties' => false ], |
| 293 | 'output_schema'=> $this->std(), |
| 294 | 'execute_callback' => function( $input = [] ) use ( $self ) { |
| 295 | if ( ! $self->form_exists( (int) $input['form_id'] ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; } |
| 296 | $r = $self->set_meta( (int) $input['form_id'], (string) $input['meta_key'], $input['value'] ); |
| 297 | if ( $r === false ) { return [ 'success' => false, 'message' => 'Save failed.' ]; } |
| 298 | return [ 'success' => true, 'message' => sprintf( 'Meta "%s" saved.', (string) $input['meta_key'] ) ]; |
| 299 | }, |
| 300 | 'permission_callback' => function() use ( $self ) { return $self->can_settings(); }, |
| 301 | 'meta' => $this->write_meta( false ), |
| 302 | ] ); |
| 303 | } |
| 304 | |
| 305 | /* ------------------------- list-notifications ---------------------- */ |
| 306 | |
| 307 | private function register_list_notifications() { |
| 308 | $self = $this; |
| 309 | wp_register_ability( 'atarim/fluent-list-notifications', [ |
| 310 | 'label' => 'List Fluent Notifications', 'category' => 'atarim', |
| 311 | 'description' => 'List a form\'s email notifications in full: { id, name, sendTo, subject, message, enabled }.', |
| 312 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ], |
| 313 | 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'notifications' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ], |
| 314 | 'execute_callback' => function( $input = [] ) { |
| 315 | $raw = AVCF_Fluent_Helpers::read_notifications_raw( (int) $input['form_id'] ); |
| 316 | $out = []; |
| 317 | foreach ( (array) $raw as $nid => $n ) { |
| 318 | if ( ! is_array( $n ) ) { continue; } |
| 319 | $out[] = [ 'id' => (string) $nid, 'name' => isset( $n['name'] ) ? (string) $n['name'] : '', 'sendTo' => isset( $n['sendTo'] ) ? $n['sendTo'] : ( isset( $n['email'] ) ? [ 'email' => (string) $n['email'] ] : null ), 'subject' => isset( $n['subject'] ) ? (string) $n['subject'] : '', 'message' => isset( $n['message'] ) ? (string) $n['message'] : '', 'enabled' => ! isset( $n['enabled'] ) || ! empty( $n['enabled'] ) ]; |
| 320 | } |
| 321 | return [ 'success' => true, 'notifications' => $out, 'message' => sprintf( '%d notification(s).', count( $out ) ) ]; |
| 322 | }, |
| 323 | 'permission_callback' => function() use ( $self ) { return $self->can_settings(); }, |
| 324 | 'meta' => $this->ro_meta(), |
| 325 | ] ); |
| 326 | } |
| 327 | |
| 328 | /* -------------------------- save-notification ---------------------- */ |
| 329 | |
| 330 | private function register_save_notification() { |
| 331 | $self = $this; |
| 332 | wp_register_ability( 'atarim/fluent-save-notification', [ |
| 333 | 'label' => 'Create/Update Fluent Notification', 'category' => 'atarim', |
| 334 | 'description' => 'Create or update an email notification. Omit notification_id to create (a numeric id is generated). Settable: name, email (recipient), subject, message, enabled. Returns the notification_id.', |
| 335 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'notification_id' => [ 'type' => 'string' ], 'name' => [ 'type' => 'string' ], 'email' => [ 'type' => 'string' ], 'subject' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ], 'enabled' => [ 'type' => 'boolean' ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ], |
| 336 | 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'notification_id' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ], |
| 337 | 'execute_callback' => function( $input = [] ) use ( $self ) { |
| 338 | if ( ! $self->form_exists( (int) $input['form_id'] ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; } |
| 339 | $notifs = AVCF_Fluent_Helpers::read_notifications_raw( (int) $input['form_id'] ); |
| 340 | if ( ! is_array( $notifs ) ) { $notifs = []; } |
| 341 | $nid = isset( $input['notification_id'] ) && $input['notification_id'] !== '' ? (string) $input['notification_id'] : (string) ( count( $notifs ) + 1 ); |
| 342 | $n = isset( $notifs[ $nid ] ) && is_array( $notifs[ $nid ] ) ? $notifs[ $nid ] : []; |
| 343 | if ( isset( $input['name'] ) ) { $n['name'] = sanitize_text_field( $input['name'] ); } |
| 344 | if ( isset( $input['subject'] ) ) { $n['subject'] = (string) $input['subject']; } |
| 345 | if ( isset( $input['message'] ) ) { $n['message'] = (string) $input['message']; } |
| 346 | if ( isset( $input['enabled'] ) ) { $n['enabled'] = ! empty( $input['enabled'] ); } |
| 347 | if ( isset( $input['email'] ) ) { |
| 348 | if ( isset( $n['sendTo'] ) && is_array( $n['sendTo'] ) ) { $n['sendTo']['email'] = (string) $input['email']; } |
| 349 | else { $n['sendTo'] = [ 'type' => 'email', 'email' => (string) $input['email'] ]; } |
| 350 | } |
| 351 | $notifs[ $nid ] = $n; |
| 352 | $r = $self->set_meta( (int) $input['form_id'], 'notifications', $notifs ); |
| 353 | if ( $r === false ) { return [ 'success' => false, 'message' => 'Save failed.' ]; } |
| 354 | return [ 'success' => true, 'notification_id' => $nid, 'message' => 'Notification saved.' ]; |
| 355 | }, |
| 356 | 'permission_callback' => function() use ( $self ) { return $self->can_settings(); }, |
| 357 | 'meta' => $this->write_meta( false ), |
| 358 | ] ); |
| 359 | } |
| 360 | |
| 361 | /* --------------------------- get-confirmation ---------------------- */ |
| 362 | |
| 363 | private function register_get_confirmation() { |
| 364 | $self = $this; |
| 365 | wp_register_ability( 'atarim/fluent-get-confirmation', [ |
| 366 | 'label' => 'Get Fluent Confirmation', 'category' => 'atarim', |
| 367 | 'description' => 'Read a form\'s confirmation/redirect settings from the formSettings meta (messageToShow, redirectTo, customPage/customUrl when set).', |
| 368 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ], |
| 369 | 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'confirmation' => [], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ], |
| 370 | 'execute_callback' => function( $input = [] ) use ( $self ) { |
| 371 | $settings = $self->get_meta( (int) $input['form_id'], 'formSettings' ); |
| 372 | if ( ! is_array( $settings ) ) { return [ 'success' => false, 'message' => 'No formSettings meta on this form.' ]; } |
| 373 | $conf = isset( $settings['confirmation'] ) ? $settings['confirmation'] : null; |
| 374 | return [ 'success' => true, 'confirmation' => $conf, 'message' => $conf === null ? 'No confirmation block found in formSettings.' : 'OK.' ]; |
| 375 | }, |
| 376 | 'permission_callback' => function() use ( $self ) { return $self->can_settings(); }, |
| 377 | 'meta' => $this->ro_meta(), |
| 378 | ] ); |
| 379 | } |
| 380 | |
| 381 | /* ----------------------- list-integration-feeds -------------------- */ |
| 382 | |
| 383 | private function register_list_integration_feeds() { |
| 384 | $self = $this; |
| 385 | wp_register_ability( 'atarim/fluent-list-integration-feeds', [ |
| 386 | 'label' => 'List Fluent Integration Feeds', 'category' => 'atarim', |
| 387 | 'description' => 'Best-effort listing of configured CRM/marketing/payment integration feeds on a form (e.g. mailchimp, slack, webhook), read from form_meta keys that look like feeds. Read-only; returns { meta_key, name, enabled } per feed where determinable.', |
| 388 | 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ], |
| 389 | 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'feeds' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ], |
| 390 | 'execute_callback' => function( $input = [] ) { |
| 391 | global $wpdb; $t = AVCF_Fluent_Helpers::meta_table(); |
| 392 | $rows = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, value FROM `{$t}` WHERE form_id = %d", (int) $input['form_id'] ) ); |
| 393 | $skip = [ 'notifications', 'formSettings', '_total_views', 'template_name', 'advancedValidationSettings', '_primary_email_field' ]; |
| 394 | $feeds = []; |
| 395 | foreach ( (array) $rows as $r ) { |
| 396 | $key = (string) $r->meta_key; |
| 397 | if ( in_array( $key, $skip, true ) ) { continue; } |
| 398 | $decoded = json_decode( $r->value, true ); |
| 399 | $looks_feed = ( strpos( $key, 'feed' ) !== false ) || ( is_array( $decoded ) && ( isset( $decoded['integration_name'] ) || isset( $decoded['list_id'] ) || isset( $decoded['feedStatus'] ) || isset( $decoded['enabled'] ) ) ); |
| 400 | if ( ! $looks_feed ) { continue; } |
| 401 | $name = is_array( $decoded ) && isset( $decoded['name'] ) ? (string) $decoded['name'] : $key; |
| 402 | $enabled = is_array( $decoded ) ? ( isset( $decoded['enabled'] ) ? (bool) $decoded['enabled'] : ( isset( $decoded['feedStatus'] ) ? (bool) $decoded['feedStatus'] : true ) ) : true; |
| 403 | $feeds[] = [ 'meta_key' => $key, 'name' => $name, 'enabled' => $enabled ]; |
| 404 | } |
| 405 | return [ 'success' => true, 'feeds' => $feeds, 'message' => sprintf( '%d integration feed(s) detected (best-effort).', count( $feeds ) ) ]; |
| 406 | }, |
| 407 | 'permission_callback' => function() use ( $self ) { return $self->can_settings(); }, |
| 408 | 'meta' => $this->ro_meta(), |
| 409 | ] ); |
| 410 | } |
| 411 | } |
| 412 |