PluginProbe ʕ •ᴥ•ʔ
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback / trunk
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback vtrunk
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 / forms / gravity-forms / class-avcf-abilities-gravity-pro.php
atarim-visual-collaboration / third-party / forms / gravity-forms Last commit date
class-avcf-abilities-gravity-pro.php 3 weeks ago class-avcf-abilities-gravity.php 3 weeks ago class-avcf-gravity-detector.php 1 month ago class-avcf-gravity-helpers.php 3 weeks ago
class-avcf-abilities-gravity-pro.php
456 lines
1 <?php
2 /**
3 * Gravity Forms — power MCP abilities (standalone cluster, -pro file).
4 *
5 * GF-specific depth beyond the common surface: form CRUD, entry CRUD, entry
6 * notes, full notification & confirmation management, and add-on feeds
7 * (payments / CRM / Zapier etc.). Writers use Gravity's own capabilities with a
8 * manage_options fallback; deletes are confirm-gated. Version-dependent GF
9 * methods are guarded and degrade to a typed error rather than fatally.
10 *
11 * Built on GFAPI / GFFormsModel; not runtime-tested here.
12 *
13 * @package atarim-visual-collaboration
14 */
15
16 if ( ! defined('ABSPATH') ) {
17 exit;
18 }
19
20 class AVCF_Abilities_Gravity_Pro extends AVCF_Abilities_Base {
21
22 /** @var AVCF_Gravity_Detector */
23 private $detector;
24
25 public function __construct() {
26 $this->detector = new AVCF_Gravity_Detector();
27 }
28
29 public function register() {
30 if ( ! $this->detector->avcf_gravity_is_available() ) {
31 return;
32 }
33 // Forms CRUD
34 $this->register_create_form();
35 $this->register_update_form();
36 $this->register_delete_form();
37 // Entries CRUD
38 $this->register_create_entry();
39 $this->register_update_entry();
40 $this->register_delete_entry();
41 // Entry notes
42 $this->register_list_entry_notes();
43 $this->register_add_entry_note();
44 // Notifications
45 $this->register_list_notifications();
46 $this->register_save_notification();
47 // Confirmations
48 $this->register_list_confirmations();
49 $this->register_save_confirmation();
50 // Feeds
51 $this->register_list_feeds();
52 $this->register_get_feed();
53 $this->register_toggle_feed();
54 }
55
56 /* ------------------------------ shared ----------------------------- */
57
58 private function write_meta( $d = false ) { return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => false, 'destructive' => (bool) $d, 'idempotent' => false ] ]; }
59 private function ro_meta() { return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => true, 'destructive' => false, 'idempotent' => true ] ]; }
60 private function std() { return [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ]; }
61
62 public function can_create_forms() { return current_user_can( 'gravityforms_create_form' ) || current_user_can( 'gravityforms_edit_forms' ) || current_user_can( 'manage_options' ); }
63 public function can_edit_forms() { return current_user_can( 'gravityforms_edit_forms' ) || current_user_can( 'manage_options' ); }
64 public function can_delete_forms() { return current_user_can( 'gravityforms_delete_forms' ) || current_user_can( 'manage_options' ); }
65 public function can_view_entries() { return current_user_can( 'gravityforms_view_entries' ) || current_user_can( 'manage_options' ); }
66 public function can_edit_entries() { return current_user_can( 'gravityforms_edit_entries' ) || current_user_can( 'manage_options' ); }
67 public function can_delete_entries() { return current_user_can( 'gravityforms_delete_entries' ) || current_user_can( 'manage_options' ); }
68
69 /* ----------------------------- create-form ------------------------- */
70
71 private function register_create_form() {
72 $self = $this;
73 wp_register_ability( 'atarim/gravity-create-form', [
74 'label' => 'Create Gravity Form', 'category' => 'atarim',
75 'description' => 'Create a new Gravity form. Provide either a full GF form definition (definition) OR a title plus a simple fields array (each: { type, label, required?, choices?[] }) — field ids are auto-assigned when omitted. Returns the new form_id.',
76 'input_schema' => [ 'type' => 'object', 'properties' => [
77 'title' => [ 'type' => 'string' ],
78 'description' => [ 'type' => 'string' ],
79 'fields' => [ 'type' => 'array', 'items' => [ 'type' => 'object' ] ],
80 'definition' => [ 'type' => 'object', 'description' => 'Full GF form array (overrides title/fields).' ],
81 ], 'additionalProperties' => false ],
82 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'form_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
83 'execute_callback' => function( $input = [] ) {
84 if ( isset( $input['definition'] ) && is_array( $input['definition'] ) ) {
85 $form = $input['definition'];
86 } else {
87 $title = isset( $input['title'] ) ? sanitize_text_field( $input['title'] ) : '';
88 if ( $title === '' ) { return [ 'success' => false, 'message' => 'Provide a title (or a full definition).' ]; }
89 $fields = []; $i = 1;
90 foreach ( ( isset( $input['fields'] ) && is_array( $input['fields'] ) ? $input['fields'] : [] ) as $f ) {
91 if ( ! is_array( $f ) ) { continue; }
92 $field = [ 'id' => isset( $f['id'] ) ? (int) $f['id'] : $i, 'type' => isset( $f['type'] ) ? (string) $f['type'] : 'text', 'label' => isset( $f['label'] ) ? (string) $f['label'] : ( 'Field ' . $i ), 'isRequired' => ! empty( $f['required'] ) ];
93 if ( isset( $f['choices'] ) && is_array( $f['choices'] ) ) {
94 $field['choices'] = [];
95 foreach ( $f['choices'] as $c ) { $txt = is_array( $c ) ? ( isset( $c['text'] ) ? $c['text'] : '' ) : (string) $c; $field['choices'][] = [ 'text' => (string) $txt, 'value' => (string) $txt ]; }
96 }
97 $fields[] = $field; $i++;
98 }
99 $form = [ 'title' => $title, 'description' => isset( $input['description'] ) ? (string) $input['description'] : '', 'fields' => $fields ];
100 }
101 $id = GFAPI::add_form( $form );
102 if ( is_wp_error( $id ) ) { return [ 'success' => false, 'message' => 'Create failed: ' . $id->get_error_message() ]; }
103 return [ 'success' => true, 'form_id' => (int) $id, 'message' => sprintf( 'Form %d created.', (int) $id ) ];
104 },
105 'permission_callback' => function() use ( $self ) { return $self->can_create_forms(); },
106 'meta' => $this->write_meta( false ),
107 ] );
108 }
109
110 /* ----------------------------- update-form ------------------------- */
111
112 private function register_update_form() {
113 $self = $this;
114 wp_register_ability( 'atarim/gravity-update-form', [
115 'label' => 'Update Gravity Form', 'category' => 'atarim',
116 'description' => 'Update a form\'s top-level properties (title, description, is_active) and/or replace its fields array. Only provided keys change; everything else is preserved.',
117 'input_schema' => [ 'type' => 'object', 'properties' => [
118 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ],
119 'title' => [ 'type' => 'string' ], 'description' => [ 'type' => 'string' ], 'is_active' => [ 'type' => 'boolean' ],
120 'fields' => [ 'type' => 'array', 'items' => [ 'type' => 'object' ], 'description' => 'Full replacement fields array (GF field definitions).' ],
121 ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
122 'output_schema'=> $this->std(),
123 'execute_callback' => function( $input = [] ) {
124 $form = GFAPI::get_form( (int) $input['form_id'] );
125 if ( ! $form ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
126 if ( isset( $input['title'] ) ) { $form['title'] = sanitize_text_field( $input['title'] ); }
127 if ( isset( $input['description'] ) ) { $form['description'] = (string) $input['description']; }
128 if ( isset( $input['is_active'] ) ) { $form['is_active'] = ! empty( $input['is_active'] ) ? 1 : 0; }
129 if ( isset( $input['fields'] ) && is_array( $input['fields'] ) ) { $form['fields'] = $input['fields']; }
130 $r = GFAPI::update_form( $form );
131 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => 'Update failed: ' . $r->get_error_message() ]; }
132 return [ 'success' => true, 'message' => 'Form updated.' ];
133 },
134 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
135 'meta' => $this->write_meta( false ),
136 ] );
137 }
138
139 /* ----------------------------- delete-form ------------------------- */
140
141 private function register_delete_form() {
142 $self = $this;
143 wp_register_ability( 'atarim/gravity-delete-form', [
144 'label' => 'Delete Gravity Form', 'category' => 'atarim',
145 'description' => 'Permanently delete a form and all its entries. DESTRUCTIVE — dry run unless confirm:true.',
146 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
147 'output_schema'=> $this->std(),
148 'execute_callback' => function( $input = [] ) {
149 $form_id = (int) $input['form_id'];
150 if ( ! GFAPI::get_form( $form_id ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
151 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => 'Dry run: re-call with confirm:true to permanently delete this form and its entries.' ]; }
152 $r = GFAPI::delete_form( $form_id );
153 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => 'Delete failed: ' . $r->get_error_message() ]; }
154 return [ 'success' => true, 'message' => sprintf( 'Form %d deleted.', $form_id ) ];
155 },
156 'permission_callback' => function() use ( $self ) { return $self->can_delete_forms(); },
157 'meta' => $this->write_meta( true ),
158 ] );
159 }
160
161 /* ---------------------------- create-entry ------------------------- */
162
163 private function register_create_entry() {
164 $self = $this;
165 wp_register_ability( 'atarim/gravity-create-entry', [
166 'label' => 'Create Gravity Entry', 'category' => 'atarim',
167 'description' => 'Add an entry to a form. values is a map of field id => value (e.g. {"1":"Jane","3":"jane@x.com"}); multi-input fields use dotted ids like "5.3". Returns the new entry_id.',
168 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'values' => [ 'type' => 'object' ] ], 'required' => [ 'form_id', 'values' ], 'additionalProperties' => false ],
169 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'entry_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
170 'execute_callback' => function( $input = [] ) {
171 $form_id = (int) $input['form_id'];
172 if ( ! GFAPI::get_form( $form_id ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
173 if ( ! is_array( $input['values'] ) ) { return [ 'success' => false, 'message' => 'values must be an object.' ]; }
174 $entry = [ 'form_id' => $form_id ];
175 foreach ( $input['values'] as $k => $v ) { $entry[ (string) $k ] = $v; }
176 $id = GFAPI::add_entry( $entry );
177 if ( is_wp_error( $id ) ) { return [ 'success' => false, 'message' => 'Create failed: ' . $id->get_error_message() ]; }
178 return [ 'success' => true, 'entry_id' => (int) $id, 'message' => sprintf( 'Entry %d created.', (int) $id ) ];
179 },
180 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); },
181 'meta' => $this->write_meta( false ),
182 ] );
183 }
184
185 /* ---------------------------- update-entry ------------------------- */
186
187 private function register_update_entry() {
188 $self = $this;
189 wp_register_ability( 'atarim/gravity-update-entry', [
190 'label' => 'Update Gravity Entry', 'category' => 'atarim',
191 'description' => 'Update field values on an existing entry. values is a map of field id => new value (merged into the entry). Only provided fields change.',
192 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'values' => [ 'type' => 'object' ] ], 'required' => [ 'entry_id', 'values' ], 'additionalProperties' => false ],
193 'output_schema'=> $this->std(),
194 'execute_callback' => function( $input = [] ) {
195 $entry = GFAPI::get_entry( (int) $input['entry_id'] );
196 if ( is_wp_error( $entry ) || ! $entry ) { return [ 'success' => false, 'message' => 'Entry not found.' ]; }
197 if ( ! is_array( $input['values'] ) ) { return [ 'success' => false, 'message' => 'values must be an object.' ]; }
198 foreach ( $input['values'] as $k => $v ) { $entry[ (string) $k ] = $v; }
199 $r = GFAPI::update_entry( $entry );
200 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => 'Update failed: ' . $r->get_error_message() ]; }
201 return [ 'success' => true, 'message' => 'Entry updated.' ];
202 },
203 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); },
204 'meta' => $this->write_meta( false ),
205 ] );
206 }
207
208 /* ---------------------------- delete-entry ------------------------- */
209
210 private function register_delete_entry() {
211 $self = $this;
212 wp_register_ability( 'atarim/gravity-delete-entry', [
213 'label' => 'Delete Gravity Entry', 'category' => 'atarim',
214 'description' => 'Permanently delete an entry. DESTRUCTIVE — dry run unless confirm:true. (To mark spam instead, use gravity-mark-entry-spam.)',
215 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'entry_id' ], 'additionalProperties' => false ],
216 'output_schema'=> $this->std(),
217 'execute_callback' => function( $input = [] ) {
218 $entry_id = (int) $input['entry_id'];
219 $entry = GFAPI::get_entry( $entry_id );
220 if ( is_wp_error( $entry ) || ! $entry ) { return [ 'success' => false, 'message' => 'Entry not found.' ]; }
221 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => 'Dry run: re-call with confirm:true to permanently delete this entry.' ]; }
222 $r = GFAPI::delete_entry( $entry_id );
223 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => 'Delete failed: ' . $r->get_error_message() ]; }
224 return [ 'success' => true, 'message' => sprintf( 'Entry %d deleted.', $entry_id ) ];
225 },
226 'permission_callback' => function() use ( $self ) { return $self->can_delete_entries(); },
227 'meta' => $this->write_meta( true ),
228 ] );
229 }
230
231 /* -------------------------- list-entry-notes ----------------------- */
232
233 private function register_list_entry_notes() {
234 $self = $this;
235 wp_register_ability( 'atarim/gravity-list-entry-notes', [
236 'label' => 'List Gravity Entry Notes', 'category' => 'atarim',
237 'description' => 'List the admin notes attached to an entry: { id, user_name, date_created, value, note_type }.',
238 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'entry_id' ], 'additionalProperties' => false ],
239 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'notes' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
240 'execute_callback' => function( $input = [] ) {
241 if ( ! class_exists( 'GFFormsModel' ) || ! method_exists( 'GFFormsModel', 'get_lead_notes' ) ) { return [ 'success' => false, 'message' => 'Gravity notes API unavailable in this version.' ]; }
242 $raw = GFFormsModel::get_lead_notes( (int) $input['entry_id'] );
243 $out = [];
244 foreach ( (array) $raw as $n ) {
245 $out[] = [ 'id' => isset( $n->id ) ? (int) $n->id : 0, 'user_name' => isset( $n->user_name ) ? (string) $n->user_name : '', 'date_created' => isset( $n->date_created ) ? (string) $n->date_created : '', 'value' => isset( $n->value ) ? (string) $n->value : '', 'note_type' => isset( $n->note_type ) ? (string) $n->note_type : '' ];
246 }
247 return [ 'success' => true, 'notes' => $out, 'message' => sprintf( '%d note(s).', count( $out ) ) ];
248 },
249 'permission_callback' => function() use ( $self ) { return $self->can_view_entries(); },
250 'meta' => $this->ro_meta(),
251 ] );
252 }
253
254 /* --------------------------- add-entry-note ------------------------ */
255
256 private function register_add_entry_note() {
257 $self = $this;
258 wp_register_ability( 'atarim/gravity-add-entry-note', [
259 'label' => 'Add Gravity Entry Note', 'category' => 'atarim',
260 'description' => 'Attach an admin note to an entry (attributed to the current user).',
261 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'note' => [ 'type' => 'string', 'minLength' => 1 ] ], 'required' => [ 'entry_id', 'note' ], 'additionalProperties' => false ],
262 'output_schema'=> $this->std(),
263 'execute_callback' => function( $input = [] ) {
264 if ( ! class_exists( 'GFFormsModel' ) || ! method_exists( 'GFFormsModel', 'add_note' ) ) { return [ 'success' => false, 'message' => 'Gravity notes API unavailable in this version.' ]; }
265 $entry = GFAPI::get_entry( (int) $input['entry_id'] );
266 if ( is_wp_error( $entry ) || ! $entry ) { return [ 'success' => false, 'message' => 'Entry not found.' ]; }
267 $user = wp_get_current_user();
268 GFFormsModel::add_note( (int) $input['entry_id'], (int) $user->ID, $user->display_name ? $user->display_name : 'system', (string) $input['note'], 'user' );
269 return [ 'success' => true, 'message' => 'Note added.' ];
270 },
271 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); },
272 'meta' => $this->write_meta( false ),
273 ] );
274 }
275
276 /* ------------------------- list-notifications ---------------------- */
277
278 private function register_list_notifications() {
279 $self = $this;
280 wp_register_ability( 'atarim/gravity-list-notifications', [
281 'label' => 'List Gravity Notifications', 'category' => 'atarim',
282 'description' => 'List a form\'s notifications in full: { id, name, event, to_type, to, subject, message, enabled, conditional_logic }.',
283 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
284 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'notifications' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
285 'execute_callback' => function( $input = [] ) {
286 $form = GFAPI::get_form( (int) $input['form_id'] );
287 if ( ! $form ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
288 $out = [];
289 foreach ( ( isset( $form['notifications'] ) && is_array( $form['notifications'] ) ? $form['notifications'] : [] ) as $nid => $n ) {
290 if ( ! is_array( $n ) ) { continue; }
291 $out[] = [ 'id' => (string) $nid, 'name' => isset( $n['name'] ) ? (string) $n['name'] : '', 'event' => isset( $n['event'] ) ? (string) $n['event'] : 'form_submission', 'to_type' => isset( $n['toType'] ) ? (string) $n['toType'] : '', 'to' => isset( $n['to'] ) ? (string) $n['to'] : '', 'subject' => isset( $n['subject'] ) ? (string) $n['subject'] : '', 'message' => isset( $n['message'] ) ? (string) $n['message'] : '', 'enabled' => ! isset( $n['isActive'] ) || ! empty( $n['isActive'] ), 'conditional_logic' => isset( $n['conditionalLogic'] ) ? $n['conditionalLogic'] : null ];
292 }
293 return [ 'success' => true, 'notifications' => $out, 'message' => sprintf( '%d notification(s).', count( $out ) ) ];
294 },
295 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
296 'meta' => $this->ro_meta(),
297 ] );
298 }
299
300 /* -------------------------- save-notification ---------------------- */
301
302 private function register_save_notification() {
303 $self = $this;
304 wp_register_ability( 'atarim/gravity-save-notification', [
305 'label' => 'Create/Update Gravity Notification', 'category' => 'atarim',
306 'description' => 'Create or update a notification on a form. Omit notification_id to create (a new id is generated). Settable: name, event, to_type (email|field|routing), to, subject, message, enabled. Returns the notification_id.',
307 'input_schema' => [ 'type' => 'object', 'properties' => [
308 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'notification_id' => [ 'type' => 'string' ],
309 'name' => [ 'type' => 'string' ], 'event' => [ 'type' => 'string' ], 'to_type' => [ 'type' => 'string' ], 'to' => [ 'type' => 'string' ],
310 'subject' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ], 'enabled' => [ 'type' => 'boolean' ],
311 ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
312 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'notification_id' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
313 'execute_callback' => function( $input = [] ) {
314 $form = GFAPI::get_form( (int) $input['form_id'] );
315 if ( ! $form ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
316 if ( ! isset( $form['notifications'] ) || ! is_array( $form['notifications'] ) ) { $form['notifications'] = []; }
317 $nid = isset( $input['notification_id'] ) && $input['notification_id'] !== '' ? (string) $input['notification_id'] : ( function_exists( 'uniqid' ) ? uniqid() : (string) wp_rand( 100000, 999999 ) );
318 $existing = isset( $form['notifications'][ $nid ] ) && is_array( $form['notifications'][ $nid ] ) ? $form['notifications'][ $nid ] : [ 'id' => $nid, 'event' => 'form_submission', 'name' => 'Notification', 'toType' => 'email' ];
319 if ( isset( $input['name'] ) ) { $existing['name'] = sanitize_text_field( $input['name'] ); }
320 if ( isset( $input['event'] ) ) { $existing['event'] = (string) $input['event']; }
321 if ( isset( $input['to_type'] ) ) { $existing['toType'] = (string) $input['to_type']; }
322 if ( isset( $input['to'] ) ) { $existing['to'] = (string) $input['to']; }
323 if ( isset( $input['subject'] ) ) { $existing['subject'] = (string) $input['subject']; }
324 if ( isset( $input['message'] ) ) { $existing['message'] = (string) $input['message']; }
325 if ( isset( $input['enabled'] ) ) { $existing['isActive'] = ! empty( $input['enabled'] ); }
326 $existing['id'] = $nid;
327 $form['notifications'][ $nid ] = $existing;
328 $r = GFAPI::update_form( $form );
329 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => 'Save failed: ' . $r->get_error_message() ]; }
330 return [ 'success' => true, 'notification_id' => $nid, 'message' => 'Notification saved.' ];
331 },
332 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
333 'meta' => $this->write_meta( false ),
334 ] );
335 }
336
337 /* ------------------------- list-confirmations ---------------------- */
338
339 private function register_list_confirmations() {
340 $self = $this;
341 wp_register_ability( 'atarim/gravity-list-confirmations', [
342 'label' => 'List Gravity Confirmations', 'category' => 'atarim',
343 'description' => 'List a form\'s confirmations: { id, name, type (message|page|redirect), is_default, message, pageId, url }.',
344 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
345 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'confirmations' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
346 'execute_callback' => function( $input = [] ) {
347 $form = GFAPI::get_form( (int) $input['form_id'] );
348 if ( ! $form ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
349 $out = [];
350 foreach ( ( isset( $form['confirmations'] ) && is_array( $form['confirmations'] ) ? $form['confirmations'] : [] ) as $cid => $c ) {
351 if ( ! is_array( $c ) ) { continue; }
352 $out[] = [ 'id' => (string) $cid, 'name' => isset( $c['name'] ) ? (string) $c['name'] : '', 'type' => isset( $c['type'] ) ? (string) $c['type'] : 'message', 'is_default' => ! empty( $c['isDefault'] ), 'message' => isset( $c['message'] ) ? (string) $c['message'] : '', 'pageId' => isset( $c['pageId'] ) ? $c['pageId'] : null, 'url' => isset( $c['url'] ) ? (string) $c['url'] : '' ];
353 }
354 return [ 'success' => true, 'confirmations' => $out, 'message' => sprintf( '%d confirmation(s).', count( $out ) ) ];
355 },
356 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
357 'meta' => $this->ro_meta(),
358 ] );
359 }
360
361 /* -------------------------- save-confirmation ---------------------- */
362
363 private function register_save_confirmation() {
364 $self = $this;
365 wp_register_ability( 'atarim/gravity-save-confirmation', [
366 'label' => 'Create/Update Gravity Confirmation', 'category' => 'atarim',
367 'description' => 'Create or update a confirmation. Omit confirmation_id to create. Settable: name, type (message|page|redirect), message, page_id, url. Returns the confirmation_id.',
368 'input_schema' => [ 'type' => 'object', 'properties' => [
369 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirmation_id' => [ 'type' => 'string' ],
370 'name' => [ 'type' => 'string' ], 'type' => [ 'type' => 'string', 'enum' => [ 'message', 'page', 'redirect' ] ],
371 'message' => [ 'type' => 'string' ], 'page_id' => [ 'type' => 'integer' ], 'url' => [ 'type' => 'string' ],
372 ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
373 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'confirmation_id' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
374 'execute_callback' => function( $input = [] ) {
375 $form = GFAPI::get_form( (int) $input['form_id'] );
376 if ( ! $form ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
377 if ( ! isset( $form['confirmations'] ) || ! is_array( $form['confirmations'] ) ) { $form['confirmations'] = []; }
378 $cid = isset( $input['confirmation_id'] ) && $input['confirmation_id'] !== '' ? (string) $input['confirmation_id'] : ( function_exists( 'uniqid' ) ? uniqid() : (string) wp_rand( 100000, 999999 ) );
379 $c = isset( $form['confirmations'][ $cid ] ) && is_array( $form['confirmations'][ $cid ] ) ? $form['confirmations'][ $cid ] : [ 'id' => $cid, 'name' => 'Confirmation', 'type' => 'message', 'isDefault' => false ];
380 if ( isset( $input['name'] ) ) { $c['name'] = sanitize_text_field( $input['name'] ); }
381 if ( isset( $input['type'] ) ) { $c['type'] = (string) $input['type']; }
382 if ( isset( $input['message'] ) ) { $c['message'] = (string) $input['message']; }
383 if ( isset( $input['page_id'] ) ) { $c['pageId'] = (int) $input['page_id']; }
384 if ( isset( $input['url'] ) ) { $c['url'] = esc_url_raw( $input['url'] ); }
385 $c['id'] = $cid;
386 $form['confirmations'][ $cid ] = $c;
387 $r = GFAPI::update_form( $form );
388 if ( is_wp_error( $r ) ) { return [ 'success' => false, 'message' => 'Save failed: ' . $r->get_error_message() ]; }
389 return [ 'success' => true, 'confirmation_id' => $cid, 'message' => 'Confirmation saved.' ];
390 },
391 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
392 'meta' => $this->write_meta( false ),
393 ] );
394 }
395
396 /* ------------------------------ feeds ------------------------------ */
397
398 private function register_list_feeds() {
399 $self = $this;
400 wp_register_ability( 'atarim/gravity-list-feeds', [
401 'label' => 'List Gravity Feeds', 'category' => 'atarim',
402 'description' => 'List add-on feeds attached to a form (payments, CRM, Zapier, etc.): { id, addon_slug, is_active, meta }. Optional addon_slug filter.',
403 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'addon_slug' => [ 'type' => 'string' ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
404 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'feeds' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
405 'execute_callback' => function( $input = [] ) {
406 if ( ! method_exists( 'GFAPI', 'get_feeds' ) ) { return [ 'success' => false, 'message' => 'Gravity feeds API unavailable.' ]; }
407 $slug = isset( $input['addon_slug'] ) && $input['addon_slug'] !== '' ? (string) $input['addon_slug'] : null;
408 $feeds = GFAPI::get_feeds( null, (int) $input['form_id'], $slug );
409 if ( is_wp_error( $feeds ) ) { return [ 'success' => true, 'feeds' => [], 'message' => 'No feeds on this form.' ]; }
410 $out = [];
411 foreach ( (array) $feeds as $f ) { $out[] = [ 'id' => isset( $f['id'] ) ? (int) $f['id'] : 0, 'addon_slug' => isset( $f['addon_slug'] ) ? (string) $f['addon_slug'] : '', 'is_active' => ! empty( $f['is_active'] ), 'meta' => isset( $f['meta'] ) ? $f['meta'] : [] ]; }
412 return [ 'success' => true, 'feeds' => $out, 'message' => sprintf( '%d feed(s).', count( $out ) ) ];
413 },
414 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
415 'meta' => $this->ro_meta(),
416 ] );
417 }
418
419 private function register_get_feed() {
420 $self = $this;
421 wp_register_ability( 'atarim/gravity-get-feed', [
422 'label' => 'Get Gravity Feed', 'category' => 'atarim',
423 'description' => 'Full config for one feed by id (addon_slug, is_active, full meta).',
424 'input_schema' => [ 'type' => 'object', 'properties' => [ 'feed_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'feed_id' ], 'additionalProperties' => false ],
425 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'feed' => [ 'type' => 'object' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
426 'execute_callback' => function( $input = [] ) {
427 if ( ! method_exists( 'GFAPI', 'get_feeds' ) ) { return [ 'success' => false, 'message' => 'Gravity feeds API unavailable.' ]; }
428 $feeds = GFAPI::get_feeds( [ (int) $input['feed_id'] ] );
429 if ( is_wp_error( $feeds ) || empty( $feeds ) ) { return [ 'success' => false, 'message' => 'Feed not found.' ]; }
430 $f = $feeds[0];
431 return [ 'success' => true, 'feed' => [ 'id' => isset( $f['id'] ) ? (int) $f['id'] : 0, 'form_id' => isset( $f['form_id'] ) ? (int) $f['form_id'] : 0, 'addon_slug' => isset( $f['addon_slug'] ) ? (string) $f['addon_slug'] : '', 'is_active' => ! empty( $f['is_active'] ), 'meta' => isset( $f['meta'] ) ? $f['meta'] : [] ], 'message' => 'OK.' ];
432 },
433 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
434 'meta' => $this->ro_meta(),
435 ] );
436 }
437
438 private function register_toggle_feed() {
439 $self = $this;
440 wp_register_ability( 'atarim/gravity-toggle-feed', [
441 'label' => 'Toggle Gravity Feed', 'category' => 'atarim',
442 'description' => 'Activate or deactivate an add-on feed by id (is_active:true/false).',
443 'input_schema' => [ 'type' => 'object', 'properties' => [ 'feed_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'is_active' => [ 'type' => 'boolean' ] ], 'required' => [ 'feed_id', 'is_active' ], 'additionalProperties' => false ],
444 'output_schema'=> $this->std(),
445 'execute_callback' => function( $input = [] ) {
446 if ( ! method_exists( 'GFAPI', 'update_feed_property' ) ) { return [ 'success' => false, 'message' => 'Gravity feed-toggle API unavailable in this version.' ]; }
447 $r = GFAPI::update_feed_property( (int) $input['feed_id'], 'is_active', ! empty( $input['is_active'] ) ? 1 : 0 );
448 if ( is_wp_error( $r ) || $r === false ) { return [ 'success' => false, 'message' => 'Toggle failed (feed not found?).' ]; }
449 return [ 'success' => true, 'message' => ! empty( $input['is_active'] ) ? 'Feed activated.' : 'Feed deactivated.' ];
450 },
451 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
452 'meta' => $this->write_meta( false ),
453 ] );
454 }
455 }
456