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 / formidable / class-avcf-abilities-formidable-pro.php
atarim-visual-collaboration / third-party / forms / formidable Last commit date
class-avcf-abilities-formidable-pro.php 4 weeks ago class-avcf-abilities-formidable.php 4 weeks ago class-avcf-formidable-detector.php 1 month ago class-avcf-formidable-helpers.php 4 weeks ago
class-avcf-abilities-formidable-pro.php
393 lines
1 <?php
2 /**
3 * Formidable Forms — power MCP abilities (standalone cluster, -pro file).
4 *
5 * Formidable-specific depth via its stable API: form CRUD (FrmForm), field
6 * read/create/delete (FrmField), entry create/update/delete/status (FrmEntry
7 * + frm_items), email-action notification management (frm_form_actions CPT),
8 * and a Pro-gated Views listing (frm_display). create-field and create-entry
9 * are experimental (Formidable's field/meta shapes are intricate); deletes are
10 * confirm-gated. Permissions use Formidable's capabilities with a manage_options
11 * fallback. Built on Formidable'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_Formidable_Pro extends AVCF_Abilities_Base {
21
22 /** @var AVCF_Formidable_Detector */
23 private $detector;
24
25 public function __construct() {
26 $this->detector = new AVCF_Formidable_Detector();
27 }
28
29 public function register() {
30 if ( ! $this->detector->avcf_formidable_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_delete_field();
39 $this->register_create_entry();
40 $this->register_update_entry();
41 $this->register_delete_entry();
42 $this->register_set_entry_status();
43 $this->register_list_notifications();
44 $this->register_save_notification();
45 $this->register_list_views();
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 is_pro() { return $this->detector->avcf_formidable_is_pro(); }
55 public function can_edit_forms() { return current_user_can( 'frm_edit_forms' ) || current_user_can( 'manage_options' ); }
56 public function can_delete_forms() { return current_user_can( 'frm_delete_forms' ) || current_user_can( 'frm_edit_forms' ) || current_user_can( 'manage_options' ); }
57 public function can_edit_entries() { return current_user_can( 'frm_edit_entries' ) || current_user_can( 'manage_options' ); }
58 public function can_delete_entries() { return current_user_can( 'frm_delete_entries' ) || current_user_can( 'frm_edit_entries' ) || current_user_can( 'manage_options' ); }
59 public function form_exists( $form_id ) { return class_exists( 'FrmForm' ) && (bool) FrmForm::getOne( (int) $form_id ); }
60
61 /* ----------------------------- create-form ------------------------- */
62
63 private function register_create_form() {
64 $self = $this;
65 wp_register_ability( 'atarim/formidable-create-form', [
66 'label' => 'Create Formidable Form', 'category' => 'atarim',
67 'description' => 'Create a new (empty) Formidable form via FrmForm::create. Provide name and optional description. Add fields afterward with formidable-create-field. Returns the new form_id.',
68 'input_schema' => [ 'type' => 'object', 'properties' => [ 'name' => [ 'type' => 'string', 'minLength' => 1 ], 'description' => [ 'type' => 'string' ] ], 'required' => [ 'name' ], 'additionalProperties' => false ],
69 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'form_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
70 'execute_callback' => function( $input = [] ) {
71 if ( ! method_exists( 'FrmForm', 'create' ) ) { return [ 'success' => false, 'message' => 'FrmForm::create unavailable.' ]; }
72 $name = sanitize_text_field( $input['name'] );
73 if ( $name === '' ) { return [ 'success' => false, 'message' => 'A name is required.' ]; }
74 $id = FrmForm::create( [ 'name' => $name, 'description' => isset( $input['description'] ) ? wp_kses_post( $input['description'] ) : '', 'status' => 'published' ] );
75 if ( ! $id ) { return [ 'success' => false, 'message' => 'Create failed.' ]; }
76 return [ 'success' => true, 'form_id' => (int) $id, 'message' => sprintf( 'Form %d created.', (int) $id ) ];
77 },
78 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
79 'meta' => $this->write_meta( false ),
80 ] );
81 }
82
83 /* ----------------------------- update-form ------------------------- */
84
85 private function register_update_form() {
86 $self = $this;
87 wp_register_ability( 'atarim/formidable-update-form', [
88 'label' => 'Update Formidable Form', 'category' => 'atarim',
89 'description' => 'Update a form\'s name, description, and/or status, plus an optional options patch (merged into the form options array). Only provided keys change.',
90 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'name' => [ 'type' => 'string' ], 'description' => [ 'type' => 'string' ], 'status' => [ 'type' => 'string', 'enum' => [ 'published', 'draft', 'trash' ] ], 'options' => [ 'type' => 'object' ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
91 'output_schema'=> $this->std(),
92 'execute_callback' => function( $input = [] ) use ( $self ) {
93 if ( ! method_exists( 'FrmForm', 'update' ) ) { return [ 'success' => false, 'message' => 'FrmForm::update unavailable.' ]; }
94 $form = FrmForm::getOne( (int) $input['form_id'] );
95 if ( ! $form ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
96 $values = [];
97 if ( isset( $input['name'] ) ) { $values['name'] = sanitize_text_field( $input['name'] ); }
98 if ( isset( $input['description'] ) ) { $values['description'] = wp_kses_post( $input['description'] ); }
99 if ( isset( $input['status'] ) ) { $values['status'] = (string) $input['status']; }
100 if ( isset( $input['options'] ) && is_array( $input['options'] ) ) {
101 $current = isset( $form->options ) && is_array( $form->options ) ? $form->options : ( is_string( $form->options ) ? maybe_unserialize( $form->options ) : [] );
102 if ( ! is_array( $current ) ) { $current = []; }
103 $values['options'] = array_merge( $current, $input['options'] );
104 }
105 if ( empty( $values ) ) { return [ 'success' => false, 'message' => 'Nothing to update.' ]; }
106 FrmForm::update( (int) $input['form_id'], $values );
107 return [ 'success' => true, 'message' => 'Form updated.' ];
108 },
109 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
110 'meta' => $this->write_meta( false ),
111 ] );
112 }
113
114 /* ----------------------------- delete-form ------------------------- */
115
116 private function register_delete_form() {
117 $self = $this;
118 wp_register_ability( 'atarim/formidable-delete-form', [
119 'label' => 'Delete Formidable Form', 'category' => 'atarim',
120 'description' => 'Permanently delete a form, its fields and entries via FrmForm::destroy. DESTRUCTIVE — dry run unless confirm:true.',
121 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
122 'output_schema'=> $this->std(),
123 'execute_callback' => function( $input = [] ) use ( $self ) {
124 if ( ! $self->form_exists( (int) $input['form_id'] ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
125 if ( ! method_exists( 'FrmForm', 'destroy' ) ) { return [ 'success' => false, 'message' => 'FrmForm::destroy unavailable.' ]; }
126 $count = AVCF_Formidable_Helpers::count_entries( (int) $input['form_id'], true );
127 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => sprintf( 'Dry run: re-call with confirm:true to delete this form, its fields, and %d entr(y/ies).', $count ) ]; }
128 $r = FrmForm::destroy( (int) $input['form_id'] );
129 return $r ? [ 'success' => true, 'message' => sprintf( 'Form %d deleted.', (int) $input['form_id'] ) ] : [ 'success' => false, 'message' => 'Delete failed.' ];
130 },
131 'permission_callback' => function() use ( $self ) { return $self->can_delete_forms(); },
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/formidable-list-fields', [
141 'label' => 'List Formidable Fields', 'category' => 'atarim',
142 'description' => 'Detailed field list for a form: { id, key, type, label, required, field_order, options, field_options }. Richer than formidable-get-form.',
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 if ( ! class_exists( 'FrmField' ) ) { return [ 'success' => false, 'message' => 'FrmField API unavailable.' ]; }
147 $raw = FrmField::get_all_for_form( (int) $input['form_id'] );
148 $out = [];
149 foreach ( (array) $raw as $f ) {
150 if ( ! isset( $f->id ) ) { continue; }
151 $fopts = isset( $f->field_options ) && is_array( $f->field_options ) ? $f->field_options : ( is_string( $f->field_options ) ? maybe_unserialize( $f->field_options ) : [] );
152 $choices = isset( $f->options ) ? ( is_string( $f->options ) ? maybe_unserialize( $f->options ) : $f->options ) : [];
153 $out[] = [ 'id' => (int) $f->id, 'key' => isset( $f->field_key ) ? (string) $f->field_key : '', 'type' => isset( $f->type ) ? (string) $f->type : '', 'label' => isset( $f->name ) ? (string) $f->name : '', 'required' => ! empty( $f->required ), 'field_order' => isset( $f->field_order ) ? (int) $f->field_order : 0, 'options' => is_array( $choices ) ? $choices : [], 'field_options' => is_array( $fopts ) ? $fopts : [] ];
154 }
155 return [ 'success' => true, 'fields' => $out, 'message' => sprintf( '%d field(s).', count( $out ) ) ];
156 },
157 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
158 'meta' => $this->ro_meta(),
159 ] );
160 }
161
162 /* ----------------------------- create-field ------------------------ */
163
164 private function register_create_field() {
165 $self = $this;
166 wp_register_ability( 'atarim/formidable-create-field', [
167 'label' => 'Create Formidable Field', 'category' => 'atarim',
168 'description' => 'Add a field to a form via FrmField::create. EXPERIMENTAL. Provide type (e.g. text, textarea, email, select, checkbox, radio), label (name), required?, and options?[] for choice fields. Returns the new field_id.',
169 '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 ], 'options' => [ 'type' => 'array' ] ], 'required' => [ 'form_id', 'type', 'label' ], 'additionalProperties' => false ],
170 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'field_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
171 'execute_callback' => function( $input = [] ) use ( $self ) {
172 if ( ! class_exists( 'FrmField' ) || ! method_exists( 'FrmField', 'create' ) ) { return [ 'success' => false, 'message' => 'FrmField::create unavailable.' ]; }
173 if ( ! $self->form_exists( (int) $input['form_id'] ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
174 $existing = FrmField::get_all_for_form( (int) $input['form_id'] );
175 $order = is_array( $existing ) ? count( $existing ) : 0;
176 $values = [ 'form_id' => (int) $input['form_id'], 'type' => (string) $input['type'], 'name' => sanitize_text_field( $input['label'] ), 'field_order' => $order, 'required' => ! empty( $input['required'] ) ? 1 : 0 ];
177 if ( isset( $input['options'] ) && is_array( $input['options'] ) && $input['options'] ) {
178 $opts = [];
179 foreach ( $input['options'] as $o ) { $txt = is_array( $o ) ? ( isset( $o['label'] ) ? $o['label'] : '' ) : (string) $o; $opts[] = (string) $txt; }
180 $values['options'] = $opts;
181 }
182 $field_id = FrmField::create( $values );
183 if ( ! $field_id ) { return [ 'success' => false, 'message' => 'Create failed (field schema may need adjusting in the editor).' ]; }
184 return [ 'success' => true, 'field_id' => (int) $field_id, 'message' => sprintf( 'Field %d created (experimental — verify in the Formidable editor).', (int) $field_id ) ];
185 },
186 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
187 'meta' => $this->write_meta( false ),
188 ] );
189 }
190
191 /* ----------------------------- delete-field ------------------------ */
192
193 private function register_delete_field() {
194 $self = $this;
195 wp_register_ability( 'atarim/formidable-delete-field', [
196 'label' => 'Delete Formidable Field', 'category' => 'atarim',
197 'description' => 'Delete a field via FrmField::destroy. DESTRUCTIVE — dry run unless confirm:true.',
198 'input_schema' => [ 'type' => 'object', 'properties' => [ 'field_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'field_id' ], 'additionalProperties' => false ],
199 'output_schema'=> $this->std(),
200 'execute_callback' => function( $input = [] ) {
201 if ( ! class_exists( 'FrmField' ) || ! method_exists( 'FrmField', 'destroy' ) ) { return [ 'success' => false, 'message' => 'FrmField::destroy unavailable.' ]; }
202 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => 'Dry run: re-call with confirm:true to delete this field and its stored values.' ]; }
203 FrmField::destroy( (int) $input['field_id'] );
204 return [ 'success' => true, 'message' => sprintf( 'Field %d deleted.', (int) $input['field_id'] ) ];
205 },
206 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
207 'meta' => $this->write_meta( true ),
208 ] );
209 }
210
211 /* ----------------------------- create-entry ------------------------ */
212
213 private function register_create_entry() {
214 $self = $this;
215 wp_register_ability( 'atarim/formidable-create-entry', [
216 'label' => 'Create Formidable Entry', 'category' => 'atarim',
217 'description' => 'Create an entry via FrmEntry::create. EXPERIMENTAL. values is a map of field_id => value. Returns the new entry_id.',
218 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'values' => [ 'type' => 'object' ] ], 'required' => [ 'form_id', 'values' ], 'additionalProperties' => false ],
219 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'entry_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
220 'execute_callback' => function( $input = [] ) use ( $self ) {
221 if ( ! class_exists( 'FrmEntry' ) || ! method_exists( 'FrmEntry', 'create' ) ) { return [ 'success' => false, 'message' => 'FrmEntry::create unavailable.' ]; }
222 if ( ! $self->form_exists( (int) $input['form_id'] ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
223 $item_meta = [];
224 foreach ( (array) $input['values'] as $fid => $val ) { $item_meta[ (int) $fid ] = $val; }
225 $entry_id = FrmEntry::create( [ 'form_id' => (int) $input['form_id'], 'item_meta' => $item_meta ] );
226 if ( ! $entry_id ) { return [ 'success' => false, 'message' => 'Create failed (check field ids/values).' ]; }
227 return [ 'success' => true, 'entry_id' => (int) $entry_id, 'message' => sprintf( 'Entry %d created.', (int) $entry_id ) ];
228 },
229 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); },
230 'meta' => $this->write_meta( false ),
231 ] );
232 }
233
234 /* ----------------------------- update-entry ------------------------ */
235
236 private function register_update_entry() {
237 $self = $this;
238 wp_register_ability( 'atarim/formidable-update-entry', [
239 'label' => 'Update Formidable Entry', 'category' => 'atarim',
240 'description' => 'Update an entry\'s field values via FrmEntry::update. values is a map of field_id => new value (only listed fields change).',
241 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'values' => [ 'type' => 'object' ] ], 'required' => [ 'entry_id', 'values' ], 'additionalProperties' => false ],
242 'output_schema'=> $this->std(),
243 'execute_callback' => function( $input = [] ) {
244 if ( ! class_exists( 'FrmEntry' ) || ! method_exists( 'FrmEntry', 'update' ) ) { return [ 'success' => false, 'message' => 'FrmEntry::update unavailable.' ]; }
245 $entry = FrmEntry::getOne( (int) $input['entry_id'] );
246 if ( ! $entry ) { return [ 'success' => false, 'message' => 'Entry not found.' ]; }
247 $item_meta = [];
248 foreach ( (array) $input['values'] as $fid => $val ) { $item_meta[ (int) $fid ] = $val; }
249 $r = FrmEntry::update( (int) $input['entry_id'], [ 'form_id' => (int) $entry->form_id, 'item_meta' => $item_meta ] );
250 if ( ! $r ) { return [ 'success' => false, 'message' => 'Update failed.' ]; }
251 return [ 'success' => true, 'message' => 'Entry updated.' ];
252 },
253 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); },
254 'meta' => $this->write_meta( false ),
255 ] );
256 }
257
258 /* ----------------------------- delete-entry ------------------------ */
259
260 private function register_delete_entry() {
261 $self = $this;
262 wp_register_ability( 'atarim/formidable-delete-entry', [
263 'label' => 'Delete Formidable Entry', 'category' => 'atarim',
264 'description' => 'Permanently delete an entry via FrmEntry::destroy. DESTRUCTIVE — dry run unless confirm:true.',
265 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'confirm' => [ 'type' => 'boolean', 'default' => false ] ], 'required' => [ 'entry_id' ], 'additionalProperties' => false ],
266 'output_schema'=> $this->std(),
267 'execute_callback' => function( $input = [] ) {
268 if ( ! class_exists( 'FrmEntry' ) || ! method_exists( 'FrmEntry', 'destroy' ) ) { return [ 'success' => false, 'message' => 'FrmEntry::destroy unavailable.' ]; }
269 if ( ! FrmEntry::getOne( (int) $input['entry_id'] ) ) { return [ 'success' => false, 'message' => 'Entry not found.' ]; }
270 if ( empty( $input['confirm'] ) ) { return [ 'success' => true, 'message' => 'Dry run: re-call with confirm:true to permanently delete this entry.' ]; }
271 FrmEntry::destroy( (int) $input['entry_id'] );
272 return [ 'success' => true, 'message' => sprintf( 'Entry %d deleted.', (int) $input['entry_id'] ) ];
273 },
274 'permission_callback' => function() use ( $self ) { return $self->can_delete_entries(); },
275 'meta' => $this->write_meta( true ),
276 ] );
277 }
278
279 /* --------------------------- set-entry-status ---------------------- */
280
281 private function register_set_entry_status() {
282 $self = $this;
283 wp_register_ability( 'atarim/formidable-set-entry-status', [
284 'label' => 'Set Formidable Entry Status', 'category' => 'atarim',
285 'description' => 'Set an entry\'s status: published (is_draft=0), draft (1), or spam (2).',
286 'input_schema' => [ 'type' => 'object', 'properties' => [ 'entry_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'status' => [ 'type' => 'string', 'enum' => [ 'published', 'draft', 'spam' ] ] ], 'required' => [ 'entry_id', 'status' ], 'additionalProperties' => false ],
287 'output_schema'=> $this->std(),
288 'execute_callback' => function( $input = [] ) {
289 global $wpdb;
290 $map = [ 'published' => 0, 'draft' => 1, 'spam' => 2 ];
291 $val = isset( $map[ (string) $input['status'] ] ) ? $map[ (string) $input['status'] ] : 0;
292 $r = $wpdb->update( AVCF_Formidable_Helpers::items_table(), [ 'is_draft' => $val ], [ 'id' => (int) $input['entry_id'] ], [ '%d' ], [ '%d' ] );
293 if ( $r === false ) { return [ 'success' => false, 'message' => 'Update failed.' ]; }
294 return [ 'success' => true, 'message' => sprintf( 'Entry status set to %s.', (string) $input['status'] ) ];
295 },
296 'permission_callback' => function() use ( $self ) { return $self->can_edit_entries(); },
297 'meta' => $this->write_meta( false ),
298 ] );
299 }
300
301 /* ------------------------- list-notifications ---------------------- */
302
303 private function register_list_notifications() {
304 $self = $this;
305 wp_register_ability( 'atarim/formidable-list-notifications', [
306 'label' => 'List Formidable Notifications', 'category' => 'atarim',
307 'description' => 'List a form\'s email-action notifications in full: { id, name, email_to, email_subject, email_message, enabled }.',
308 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
309 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'notifications' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
310 'execute_callback' => function( $input = [] ) {
311 if ( ! class_exists( 'FrmFormAction' ) ) { return [ 'success' => false, 'message' => 'FrmFormAction unavailable.' ]; }
312 $actions = FrmFormAction::get_action_for_form( (int) $input['form_id'], 'email' );
313 if ( ! is_array( $actions ) ) { $actions = []; }
314 $out = [];
315 foreach ( $actions as $a ) {
316 if ( ! is_object( $a ) ) { continue; }
317 $aid = isset( $a->ID ) ? (string) $a->ID : ( isset( $a->id ) ? (string) $a->id : '' );
318 $s = isset( $a->post_content ) && is_array( $a->post_content ) ? $a->post_content : [];
319 $out[] = [ 'id' => $aid, 'name' => isset( $a->post_title ) ? (string) $a->post_title : '', 'email_to' => isset( $s['email_to'] ) ? (string) $s['email_to'] : '', 'email_subject' => isset( $s['email_subject'] ) ? (string) $s['email_subject'] : '', 'email_message' => isset( $s['email_message'] ) ? (string) $s['email_message'] : '', 'enabled' => ! ( isset( $a->post_status ) && $a->post_status === 'draft' ) ];
320 }
321 return [ 'success' => true, 'notifications' => $out, 'message' => sprintf( '%d notification(s).', count( $out ) ) ];
322 },
323 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
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/formidable-save-notification', [
333 'label' => 'Create/Update Formidable Notification', 'category' => 'atarim',
334 'description' => 'Create or update an email-action notification (stored as a frm_form_actions post). Omit notification_id to create a new email action. Settable: name, email_to, email_subject, email_message, enabled. Returns the notification_id. Creation is best-effort.',
335 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'notification_id' => [ 'type' => 'integer' ], 'name' => [ 'type' => 'string' ], 'email_to' => [ 'type' => 'string' ], 'email_subject' => [ 'type' => 'string' ], 'email_message' => [ 'type' => 'string' ], 'enabled' => [ 'type' => 'boolean' ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
336 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'notification_id' => [ 'type' => 'integer' ], '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 $is_update = isset( $input['notification_id'] ) && (int) $input['notification_id'] > 0;
340 if ( $is_update ) {
341 $post_id = (int) $input['notification_id'];
342 $post = get_post( $post_id );
343 if ( ! $post || $post->post_type !== 'frm_form_actions' ) { return [ 'success' => false, 'message' => 'Notification action not found.' ]; }
344 $settings = json_decode( $post->post_content, true );
345 if ( ! is_array( $settings ) ) { $settings = []; }
346 } else {
347 $settings = [ 'email_to' => '[admin_email]', 'event' => [ 'create' ] ];
348 $post_id = 0;
349 }
350 if ( isset( $input['email_to'] ) ) { $settings['email_to'] = (string) $input['email_to']; }
351 if ( isset( $input['email_subject'] ) ) { $settings['email_subject'] = (string) $input['email_subject']; }
352 if ( isset( $input['email_message'] ) ) { $settings['email_message'] = (string) $input['email_message']; }
353 $post_status = isset( $input['enabled'] ) && empty( $input['enabled'] ) ? 'draft' : 'publish';
354 $title = isset( $input['name'] ) ? sanitize_text_field( $input['name'] ) : 'Email Notification';
355 $postarr = [ 'post_type' => 'frm_form_actions', 'post_title' => $title, 'post_excerpt' => 'email', 'menu_order' => (int) $input['form_id'], 'post_status' => $post_status, 'post_content' => wp_slash( wp_json_encode( $settings ) ) ];
356 if ( $is_update ) { $postarr['ID'] = $post_id; $rid = wp_update_post( $postarr, true ); }
357 else { $rid = wp_insert_post( $postarr, true ); }
358 if ( is_wp_error( $rid ) || ! $rid ) { return [ 'success' => false, 'message' => is_wp_error( $rid ) ? $rid->get_error_message() : 'Save failed.' ]; }
359 return [ 'success' => true, 'notification_id' => (int) $rid, 'message' => $is_update ? 'Notification updated.' : 'Notification created (verify in the Formidable editor).' ];
360 },
361 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
362 'meta' => $this->write_meta( false ),
363 ] );
364 }
365
366 /* ------------------------------- views ----------------------------- */
367
368 private function register_list_views() {
369 $self = $this;
370 wp_register_ability( 'atarim/formidable-list-views', [
371 'label' => 'List Formidable Views', 'category' => 'atarim',
372 'description' => 'List Formidable Views (frm_display CPT): { id, title, status, form_id }. Requires Formidable Pro (Views).',
373 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'limit' => [ 'type' => 'integer', 'minimum' => 1, 'maximum' => 200, 'default' => 100 ] ], 'additionalProperties' => false ],
374 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'views' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
375 'execute_callback' => function( $input = [] ) use ( $self ) {
376 if ( ! $self->is_pro() ) { return [ 'success' => false, 'message' => 'Formidable Views require Formidable Pro.' ]; }
377 if ( ! post_type_exists( 'frm_display' ) ) { return [ 'success' => false, 'message' => 'Views post type (frm_display) not registered.' ]; }
378 $posts = get_posts( [ 'post_type' => 'frm_display', 'post_status' => 'any', 'numberposts' => isset( $input['limit'] ) ? max( 1, (int) $input['limit'] ) : 100 ] );
379 $want_form = isset( $input['form_id'] ) ? (int) $input['form_id'] : 0;
380 $out = [];
381 foreach ( (array) $posts as $p ) {
382 $linked = (int) get_post_meta( $p->ID, 'frm_form_id', true );
383 if ( $want_form > 0 && $linked !== $want_form ) { continue; }
384 $out[] = [ 'id' => (int) $p->ID, 'title' => (string) $p->post_title, 'status' => (string) $p->post_status, 'form_id' => $linked ];
385 }
386 return [ 'success' => true, 'views' => $out, 'message' => sprintf( '%d view(s).', count( $out ) ) ];
387 },
388 'permission_callback' => function() use ( $self ) { return $self->can_edit_forms(); },
389 'meta' => $this->ro_meta(),
390 ] );
391 }
392 }
393