PluginProbe ʕ •ᴥ•ʔ
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback / 5.0
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback v5.0
5.1.3 5.1.2 5.1.1 5.1 5.0 trunk 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 3.2.0 3.2.1 3.22 3.22.1 3.22.2 3.22.3 3.22.4 3.22.5 3.22.6 3.3.0 3.3.1 3.3.2 3.3.2.1 3.3.2.2 3.3.3 3.30 3.31 3.32 3.4 3.4.1 3.4.3 3.4.4 3.5 3.5.1 3.6 3.6.1 3.7 3.8 3.9 3.9.1 3.9.2 3.9.3 3.9.4 3.9.6 3.9.6.1 4.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.2 4.2.1 4.2.2 4.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.4
atarim-visual-collaboration / third-party / forms / ninja-forms / class-avcf-abilities-ninja-pro.php
atarim-visual-collaboration / third-party / forms / ninja-forms Last commit date
class-avcf-abilities-ninja-pro.php 1 month ago class-avcf-abilities-ninja.php 1 month ago class-avcf-ninja-detector.php 1 month ago class-avcf-ninja-helpers.php 1 month ago
class-avcf-abilities-ninja-pro.php
395 lines
1 <?php
2 /**
3 * Ninja Forms — power MCP abilities (standalone cluster, -pro file).
4 *
5 * Ninja-specific depth via the Ninja_Forms() model API: form create/update/
6 * delete, field list/create/update/delete, submission delete + bulk-delete,
7 * action listing/toggling, email-notification save, and a best-effort form JSON
8 * export. Ninja's model methods vary across versions, so every model call is
9 * guarded with method_exists; create-form and create-field are experimental;
10 * deletes are confirm-gated. Permission baseline is manage_options. Built on
11 * Ninja's API; not runtime-tested here.
12 *
13 * @package atarim-visual-collaboration
14 */
15
16 if ( ! defined('ABSPATH') ) {
17 exit;
18 }
19
20 class AVCF_Abilities_Ninja_Pro extends AVCF_Abilities_Base {
21
22 /** @var AVCF_Ninja_Detector */
23 private $detector;
24
25 public function __construct() {
26 $this->detector = new AVCF_Ninja_Detector();
27 }
28
29 public function register() {
30 if ( ! $this->detector->avcf_ninja_is_available() ) {
31 return;
32 }
33 $this->register_create_form();
34 $this->register_update_form();
35 $this->register_delete_form();
36 $this->register_list_fields();
37 $this->register_create_field();
38 $this->register_update_field();
39 $this->register_delete_field();
40 $this->register_delete_entry();
41 $this->register_bulk_delete_entries();
42 $this->register_list_actions();
43 $this->register_toggle_action();
44 $this->register_save_notification();
45 $this->register_export_form();
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_manage() { return current_user_can( 'manage_options' ); }
55
56 public function form_model( $form_id ) {
57 $m = Ninja_Forms()->form( (int) $form_id )->get();
58 if ( ! $m || ! method_exists( $m, 'get_setting' ) ) { return null; }
59 if ( method_exists( $m, 'get_id' ) && ! $m->get_id() && (string) $m->get_setting( 'title' ) === '' ) { return null; }
60 return $m;
61 }
62
63 /* ----------------------------- create-form ------------------------- */
64
65 private function register_create_form() {
66 $self = $this;
67 wp_register_ability( 'atarim/ninja-create-form', [
68 'label' => 'Create Ninja Form', 'category' => 'atarim',
69 'description' => 'Create a new (empty) Ninja form. EXPERIMENTAL. Provide title. Add fields afterward with ninja-create-field. Returns the new form_id.',
70 'input_schema' => [ 'type' => 'object', 'properties' => [ 'title' => [ 'type' => 'string', 'minLength' => 1 ] ], 'required' => [ 'title' ], 'additionalProperties' => false ],
71 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'form_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
72 'execute_callback' => function( $input = [] ) {
73 $title = sanitize_text_field( $input['title'] );
74 if ( $title === '' ) { return [ 'success' => false, 'message' => 'A title is required.' ]; }
75 $form = Ninja_Forms()->form()->get();
76 if ( ! $form || ! method_exists( $form, 'update_setting' ) || ! method_exists( $form, 'save' ) ) { return [ 'success' => false, 'message' => 'Ninja form model not creatable in this version.' ]; }
77 $form->update_setting( 'title', $title );
78 $form->save();
79 $new_id = method_exists( $form, 'get_id' ) ? (int) $form->get_id() : 0;
80 if ( ! $new_id ) { return [ 'success' => false, 'message' => 'Create failed.' ]; }
81 return [ 'success' => true, 'form_id' => $new_id, 'message' => sprintf( 'Form %d created (experimental — verify in the Ninja editor).', $new_id ) ];
82 },
83 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
84 'meta' => $this->write_meta( false ),
85 ] );
86 }
87
88 /* ----------------------------- update-form ------------------------- */
89
90 private function register_update_form() {
91 $self = $this;
92 wp_register_ability( 'atarim/ninja-update-form', [
93 'label' => 'Update Ninja Form', 'category' => 'atarim',
94 'description' => 'Update a form\'s title and/or arbitrary form-level settings (settings is a map of setting_key => value). Only provided keys change.',
95 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'title' => [ 'type' => 'string' ], 'settings' => [ 'type' => 'object' ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
96 'output_schema'=> $this->std(),
97 'execute_callback' => function( $input = [] ) use ( $self ) {
98 $form = $self->form_model( (int) $input['form_id'] );
99 if ( ! $form ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
100 if ( ! method_exists( $form, 'update_setting' ) || ! method_exists( $form, 'save' ) ) { return [ 'success' => false, 'message' => 'Form not editable in this version.' ]; }
101 if ( isset( $input['title'] ) ) { $form->update_setting( 'title', sanitize_text_field( $input['title'] ) ); }
102 if ( isset( $input['settings'] ) && is_array( $input['settings'] ) ) { foreach ( $input['settings'] as $k => $v ) { $form->update_setting( (string) $k, $v ); } }
103 $form->save();
104 return [ 'success' => true, 'message' => 'Form updated.' ];
105 },
106 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
107 'meta' => $this->write_meta( false ),
108 ] );
109 }
110
111 /* ----------------------------- delete-form ------------------------- */
112
113 private function register_delete_form() {
114 $self = $this;
115 wp_register_ability( 'atarim/ninja-delete-form', [
116 'label' => 'Delete Ninja Form', 'category' => 'atarim',
117 'description' => 'Permanently delete a form and its fields/actions. DESTRUCTIVE — dry run unless confirm:true.',
118 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
119 'output_schema'=> $this->std(),
120 'execute_callback' => function( $input = [] ) use ( $self ) {
121 $form = $self->form_model( (int) $input['form_id'] );
122 if ( ! $form ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
123 $count = AVCF_Ninja_Helpers::count_submissions( (int) $input['form_id'], true );
124 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => sprintf( 'Dry run: re-call with confirm:true to delete this form (it has %d submission(s), which remain as orphaned nf_sub posts).', $count ) ]; }
125 $factory = Ninja_Forms()->form( (int) $input['form_id'] );
126 if ( method_exists( $factory, 'delete' ) ) { $factory->delete(); }
127 elseif ( method_exists( $form, 'delete' ) ) { $form->delete(); }
128 else { return [ 'success' => false, 'message' => 'No delete method available in this version.' ]; }
129 return [ 'success' => true, 'message' => sprintf( 'Form %d deleted.', (int) $input['form_id'] ) ];
130 },
131 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
132 'meta' => $this->write_meta( true ),
133 ] );
134 }
135
136 /* ----------------------------- list-fields ------------------------- */
137
138 private function register_list_fields() {
139 $self = $this;
140 wp_register_ability( 'atarim/ninja-list-fields', [
141 'label' => 'List Ninja Fields', 'category' => 'atarim',
142 'description' => 'Detailed field list for a form: { id, key, type, label, required, settings } (full settings included).',
143 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
144 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'fields' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
145 'execute_callback' => function( $input = [] ) {
146 $models = Ninja_Forms()->form( (int) $input['form_id'] )->get_fields();
147 $out = [];
148 foreach ( (array) $models as $fid => $fm ) {
149 if ( ! is_object( $fm ) || ! method_exists( $fm, 'get_settings' ) ) { continue; }
150 $s = $fm->get_settings();
151 $out[] = [ 'id' => (string) $fid, 'key' => isset( $s['key'] ) ? (string) $s['key'] : '', 'type' => isset( $s['type'] ) ? (string) $s['type'] : '', 'label' => isset( $s['label'] ) ? (string) $s['label'] : '', 'required' => ! empty( $s['required'] ), 'settings' => $s ];
152 }
153 return [ 'success' => true, 'fields' => $out, 'message' => sprintf( '%d field(s).', count( $out ) ) ];
154 },
155 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
156 'meta' => $this->ro_meta(),
157 ] );
158 }
159
160 /* ----------------------------- create-field ------------------------ */
161
162 private function register_create_field() {
163 $self = $this;
164 wp_register_ability( 'atarim/ninja-create-field', [
165 'label' => 'Create Ninja Field', 'category' => 'atarim',
166 'description' => 'Add a field to a form. EXPERIMENTAL. Provide type (e.g. textbox, email, textarea, listselect, checkbox), label, required?, key?. Returns the new field_id.',
167 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'type' => [ 'type' => 'string', 'minLength' => 1 ], 'label' => [ 'type' => 'string', 'minLength' => 1 ], 'required' => [ 'type' => 'boolean', 'default' => false ], 'key' => [ 'type' => 'string' ] ], 'required' => [ 'form_id', 'type', 'label' ], 'additionalProperties' => false ],
168 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'field_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
169 'execute_callback' => function( $input = [] ) use ( $self ) {
170 if ( ! $self->form_model( (int) $input['form_id'] ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
171 $factory = Ninja_Forms()->form( (int) $input['form_id'] );
172 if ( ! method_exists( $factory, 'field' ) ) { return [ 'success' => false, 'message' => 'Field creation unavailable in this version.' ]; }
173 $field = $factory->field()->get();
174 if ( ! $field || ! method_exists( $field, 'update_setting' ) || ! method_exists( $field, 'save' ) ) { return [ 'success' => false, 'message' => 'Field model not creatable in this version.' ]; }
175 $key = isset( $input['key'] ) && $input['key'] !== '' ? sanitize_key( $input['key'] ) : ( sanitize_key( $input['label'] ) . '_' . wp_rand( 100, 999 ) );
176 $field->update_setting( 'type', (string) $input['type'] );
177 $field->update_setting( 'label', sanitize_text_field( $input['label'] ) );
178 $field->update_setting( 'key', $key );
179 $field->update_setting( 'required', ! empty( $input['required'] ) ? 1 : 0 );
180 if ( method_exists( $field, 'update_setting' ) ) { $field->update_setting( 'parent_id', (int) $input['form_id'] ); }
181 $field->save();
182 $fid = method_exists( $field, 'get_id' ) ? (int) $field->get_id() : 0;
183 if ( ! $fid ) { return [ 'success' => false, 'message' => 'Create failed.' ]; }
184 return [ 'success' => true, 'field_id' => $fid, 'message' => sprintf( 'Field %d created (experimental — verify in the Ninja editor).', $fid ) ];
185 },
186 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
187 'meta' => $this->write_meta( false ),
188 ] );
189 }
190
191 /* ----------------------------- update-field ------------------------ */
192
193 private function register_update_field() {
194 $self = $this;
195 wp_register_ability( 'atarim/ninja-update-field', [
196 'label' => 'Update Ninja Field', 'category' => 'atarim',
197 'description' => 'Patch a field\'s settings (settings is a map of setting_key => value, e.g. label, required, placeholder). Only provided keys change.',
198 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'field_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'settings' => [ 'type' => 'object' ] ], 'required' => [ 'form_id', 'field_id', 'settings' ], 'additionalProperties' => false ],
199 'output_schema'=> $this->std(),
200 'execute_callback' => function( $input = [] ) {
201 $fields = Ninja_Forms()->form( (int) $input['form_id'] )->get_fields();
202 $fid = (string) $input['field_id'];
203 if ( ! isset( $fields[ $fid ] ) ) { return [ 'success' => false, 'message' => 'Field not found on this form.' ]; }
204 $field = $fields[ $fid ];
205 if ( ! method_exists( $field, 'update_setting' ) || ! method_exists( $field, 'save' ) ) { return [ 'success' => false, 'message' => 'Field not editable in this version.' ]; }
206 foreach ( (array) $input['settings'] as $k => $v ) { $field->update_setting( (string) $k, $v ); }
207 $field->save();
208 return [ 'success' => true, 'message' => 'Field updated.' ];
209 },
210 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
211 'meta' => $this->write_meta( false ),
212 ] );
213 }
214
215 /* ----------------------------- delete-field ------------------------ */
216
217 private function register_delete_field() {
218 $self = $this;
219 wp_register_ability( 'atarim/ninja-delete-field', [
220 'label' => 'Delete Ninja Field', 'category' => 'atarim',
221 'description' => 'Delete a field from a form. DESTRUCTIVE — dry run unless confirm:true.',
222 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'field_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'form_id', 'field_id' ], 'additionalProperties' => false ],
223 'output_schema'=> $this->std(),
224 'execute_callback' => function( $input = [] ) {
225 $fields = Ninja_Forms()->form( (int) $input['form_id'] )->get_fields();
226 $fid = (string) $input['field_id'];
227 if ( ! isset( $fields[ $fid ] ) ) { return [ 'success' => false, 'message' => 'Field not found on this form.' ]; }
228 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => 'Dry run: re-call with confirm:true to delete this field and its stored values.' ]; }
229 $field = $fields[ $fid ];
230 if ( ! method_exists( $field, 'delete' ) ) { return [ 'success' => false, 'message' => 'Field delete unavailable in this version.' ]; }
231 $field->delete();
232 return [ 'success' => true, 'message' => sprintf( 'Field %d deleted.', (int) $input['field_id'] ) ];
233 },
234 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
235 'meta' => $this->write_meta( true ),
236 ] );
237 }
238
239 /* ----------------------------- delete-entry ------------------------ */
240
241 private function register_delete_entry() {
242 $self = $this;
243 wp_register_ability( 'atarim/ninja-delete-entry', [
244 'label' => 'Delete Ninja Entry', 'category' => 'atarim',
245 'description' => 'Permanently delete a submission (nf_sub post). DESTRUCTIVE — dry run unless confirm:true.',
246 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'entry_id' ], 'additionalProperties' => false ],
247 'output_schema'=> $this->std(),
248 'execute_callback' => function( $input = [] ) {
249 $entry_id = (int) $input['entry_id'];
250 $post = get_post( $entry_id );
251 if ( ! $post || $post->post_type !== 'nf_sub' ) { return [ 'success' => false, 'message' => 'Submission not found.' ]; }
252 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => 'Dry run: re-call with confirm:true to permanently delete this submission.' ]; }
253 $r = wp_delete_post( $entry_id, true );
254 return $r ? [ 'success' => true, 'message' => sprintf( 'Submission %d deleted.', $entry_id ) ] : [ 'success' => false, 'message' => 'Delete failed.' ];
255 },
256 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
257 'meta' => $this->write_meta( true ),
258 ] );
259 }
260
261 /* -------------------------- bulk-delete-entries -------------------- */
262
263 private function register_bulk_delete_entries() {
264 $self = $this;
265 wp_register_ability( 'atarim/ninja-bulk-delete-entries', [
266 'label' => 'Bulk Delete Ninja Entries', 'category' => 'atarim',
267 'description' => 'Permanently delete multiple submissions. Provide entry_ids[]. DESTRUCTIVE — dry run unless confirm:true.',
268 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_ids' => [ 'type' => 'array', 'items' => [ 'type' => 'integer' ], 'minItems' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'entry_ids' ], 'additionalProperties' => false ],
269 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'deleted' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
270 'execute_callback' => function( $input = [] ) {
271 $ids = array_values( array_filter( array_map( 'intval', (array) $input['entry_ids'] ) ) );
272 if ( empty( $ids ) ) { return [ 'success' => false, 'message' => 'No valid entry ids.' ]; }
273 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => sprintf( 'Dry run: re-call with confirm:true to permanently delete %d submission(s).', count( $ids ) ) ]; }
274 $deleted = 0;
275 foreach ( $ids as $id ) {
276 $post = get_post( $id );
277 if ( $post && $post->post_type === 'nf_sub' && wp_delete_post( $id, true ) ) { $deleted++; }
278 }
279 return [ 'success' => true, 'deleted' => $deleted, 'message' => sprintf( '%d submission(s) deleted.', $deleted ) ];
280 },
281 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
282 'meta' => $this->write_meta( true ),
283 ] );
284 }
285
286 /* ----------------------------- list-actions ------------------------ */
287
288 private function register_list_actions() {
289 $self = $this;
290 wp_register_ability( 'atarim/ninja-list-actions', [
291 'label' => 'List Ninja Actions', 'category' => 'atarim',
292 'description' => 'List all actions on a form (not just email): { id, type, label, active }. Actions include email, store-submission, redirect, webhooks, integrations, etc.',
293 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
294 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'actions' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
295 'execute_callback' => function( $input = [] ) {
296 $out = [];
297 foreach ( (array) Ninja_Forms()->form( (int) $input['form_id'] )->get_actions() as $aid => $a ) {
298 if ( ! is_object( $a ) || ! method_exists( $a, 'get_settings' ) ) { continue; }
299 $s = $a->get_settings();
300 $out[] = [ 'id' => (string) $aid, 'type' => isset( $s['type'] ) ? (string) $s['type'] : '', 'label' => isset( $s['label'] ) ? (string) $s['label'] : '', 'active' => ! isset( $s['active'] ) || ! empty( $s['active'] ) ];
301 }
302 return [ 'success' => true, 'actions' => $out, 'message' => sprintf( '%d action(s).', count( $out ) ) ];
303 },
304 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
305 'meta' => $this->ro_meta(),
306 ] );
307 }
308
309 /* ----------------------------- toggle-action ----------------------- */
310
311 private function register_toggle_action() {
312 $self = $this;
313 wp_register_ability( 'atarim/ninja-toggle-action', [
314 'label' => 'Toggle Ninja Action', 'category' => 'atarim',
315 'description' => 'Enable or disable an action (active:true/false).',
316 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'action_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'active' => [ 'type' => 'boolean' ] ], 'required' => [ 'form_id', 'action_id', 'active' ], 'additionalProperties' => false ],
317 'output_schema'=> $this->std(),
318 'execute_callback' => function( $input = [] ) {
319 $actions = Ninja_Forms()->form( (int) $input['form_id'] )->get_actions();
320 $aid = (string) $input['action_id'];
321 if ( ! isset( $actions[ $aid ] ) ) { return [ 'success' => false, 'message' => 'Action not found.' ]; }
322 $action = $actions[ $aid ];
323 if ( ! method_exists( $action, 'update_setting' ) || ! method_exists( $action, 'save' ) ) { return [ 'success' => false, 'message' => 'Action not editable in this version.' ]; }
324 $action->update_setting( 'active', ! empty( $input['active'] ) ? 1 : 0 );
325 $action->save();
326 return [ 'success' => true, 'message' => ! empty( $input['active'] ) ? 'Action enabled.' : 'Action disabled.' ];
327 },
328 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
329 'meta' => $this->write_meta( false ),
330 ] );
331 }
332
333 /* -------------------------- save-notification ---------------------- */
334
335 private function register_save_notification() {
336 $self = $this;
337 wp_register_ability( 'atarim/ninja-save-notification', [
338 'label' => 'Create/Update Ninja Notification', 'category' => 'atarim',
339 'description' => 'Create or update an email action. Omit action_id to create a new email action. Settable: label, to (recipients), email_subject, email_message. Returns the action_id. Creation is best-effort.',
340 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'action_id' => [ 'type' => 'integer' ], 'label' => [ 'type' => 'string' ], 'to' => [ 'type' => 'string' ], 'email_subject' => [ 'type' => 'string' ], 'email_message' => [ 'type' => 'string' ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
341 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'action_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
342 'execute_callback' => function( $input = [] ) use ( $self ) {
343 if ( ! $self->form_model( (int) $input['form_id'] ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
344 $factory = Ninja_Forms()->form( (int) $input['form_id'] );
345 $action = null;
346 if ( isset( $input['action_id'] ) && (int) $input['action_id'] > 0 ) {
347 $actions = $factory->get_actions();
348 $aid = (string) $input['action_id'];
349 if ( ! isset( $actions[ $aid ] ) ) { return [ 'success' => false, 'message' => 'Action not found.' ]; }
350 $action = $actions[ $aid ];
351 } else {
352 if ( ! method_exists( $factory, 'action' ) ) { return [ 'success' => false, 'message' => 'Action creation unavailable in this version.' ]; }
353 $action = $factory->action()->get();
354 if ( $action && method_exists( $action, 'update_setting' ) ) { $action->update_setting( 'type', 'email' ); $action->update_setting( 'parent_id', (int) $input['form_id'] ); }
355 }
356 if ( ! $action || ! method_exists( $action, 'update_setting' ) || ! method_exists( $action, 'save' ) ) { return [ 'success' => false, 'message' => 'Action model not editable in this version.' ]; }
357 if ( isset( $input['label'] ) ) { $action->update_setting( 'label', sanitize_text_field( $input['label'] ) ); }
358 if ( isset( $input['to'] ) ) { $action->update_setting( 'to', (string) $input['to'] ); }
359 if ( isset( $input['email_subject'] ) ) { $action->update_setting( 'email_subject', (string) $input['email_subject'] ); }
360 if ( isset( $input['email_message'] ) ) { $action->update_setting( 'email_message', (string) $input['email_message'] ); }
361 $action->save();
362 $aid = method_exists( $action, 'get_id' ) ? (int) $action->get_id() : 0;
363 return [ 'success' => true, 'action_id' => $aid, 'message' => 'Notification saved (verify in the Ninja editor).' ];
364 },
365 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
366 'meta' => $this->write_meta( false ),
367 ] );
368 }
369
370 /* ----------------------------- export-form ------------------------- */
371
372 private function register_export_form() {
373 $self = $this;
374 wp_register_ability( 'atarim/ninja-export-form', [
375 'label' => 'Export Ninja Form', 'category' => 'atarim',
376 'description' => 'Export a form definition as a Ninja JSON structure (for backup/migration). Best-effort: uses NF_Database_Models_Form::export when available, otherwise the form settings.',
377 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
378 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'export' => [], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
379 'execute_callback' => function( $input = [] ) use ( $self ) {
380 $form_id = (int) $input['form_id'];
381 if ( ! $self->form_model( $form_id ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
382 if ( class_exists( 'NF_Database_Models_Form' ) && method_exists( 'NF_Database_Models_Form', 'export' ) ) {
383 $export = NF_Database_Models_Form::export( $form_id, true );
384 if ( $export ) { return [ 'success' => true, 'export' => $export, 'message' => 'OK.' ]; }
385 }
386 $model = Ninja_Forms()->form( $form_id )->get();
387 $settings = ( $model && method_exists( $model, 'get_settings' ) ) ? $model->get_settings() : [];
388 return [ 'success' => true, 'export' => [ 'settings' => $settings, 'fields' => AVCF_Ninja_Helpers::map_fields( $form_id ) ], 'message' => 'Exported form settings + fields (native export unavailable).' ];
389 },
390 'permission_callback' => function() use ( $self ) { return $self->can_manage(); },
391 'meta' => $this->ro_meta(),
392 ] );
393 }
394 }
395