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 / contact-form-7 / class-avcf-abilities-cf7.php
atarim-visual-collaboration / third-party / forms / contact-form-7 Last commit date
class-avcf-abilities-cf7-pro.php 3 weeks ago class-avcf-abilities-cf7.php 3 weeks ago class-avcf-cf7-detector.php 1 month ago class-avcf-cf7-helpers.php 1 month ago
class-avcf-abilities-cf7.php
233 lines
1 <?php
2 /**
3 * Contact Form 7 — core MCP abilities (standalone cluster).
4 *
5 * CF7's native surface is config-only (it stores no submissions), so the core
6 * is shaped to CF7's reality: form list/get, the [form] template, the mail /
7 * mail_2 notification templates, the messages, additional settings, plus the
8 * two writes from the common surface (notification recipients, duplicate).
9 * Config mutation and a Flamingo-backed entry bridge live in the -pro file.
10 * Permissions use CF7's capabilities with a manage_options fallback. Built on
11 * CF7's model; not runtime-tested here.
12 *
13 * @package atarim-visual-collaboration
14 */
15
16 if ( ! defined('ABSPATH') ) {
17 exit;
18 }
19
20 class AVCF_Abilities_CF7 extends AVCF_Abilities_Base {
21
22 /** @var AVCF_CF7_Detector */
23 private $detector;
24
25 public function __construct() {
26 $this->detector = new AVCF_CF7_Detector();
27 }
28
29 public function register() {
30 if ( ! $this->detector->avcf_cf7_is_available() ) {
31 return;
32 }
33 $this->register_list_forms();
34 $this->register_get_form();
35 $this->register_get_form_template();
36 $this->register_list_mail_templates();
37 $this->register_get_messages();
38 $this->register_get_additional_settings();
39 $this->register_update_notifications();
40 $this->register_duplicate_form();
41 }
42
43 /* ------------------------------ shared ----------------------------- */
44
45 private function ro_meta() { return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => true, 'destructive' => false, 'idempotent' => true ] ]; }
46 private function write_meta( $d = false ) { return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => false, 'destructive' => (bool) $d, 'idempotent' => false ] ]; }
47
48 public function can_read() { return current_user_can( 'wpcf7_read_contact_forms' ) || current_user_can( 'wpcf7_edit_contact_forms' ) || current_user_can( 'manage_options' ); }
49 public function can_edit() { return current_user_can( 'wpcf7_edit_contact_forms' ) || current_user_can( 'manage_options' ); }
50
51 /* ---------------------------- list-forms --------------------------- */
52
53 private function register_list_forms() {
54 $self = $this;
55 wp_register_ability( 'atarim/cf7-list-forms', [
56 'label' => 'List Contact Form 7 Forms', 'category' => 'atarim',
57 'description' => 'List CF7 forms: { id, title, status, entries_total, last_submission, created_at }. CF7 stores no entries itself, so entries_total/last_submission are always null here — use the Flamingo abilities (atarim/flamingo-*) for submission data, passing this form_id. Optional search, limit, offset.',
58 'input_schema' => [ 'type' => 'object', 'properties' => [ 'search' => [ 'type' => 'string' ], 'limit' => [ 'type' => 'integer', 'minimum' => 1, 'maximum' => 200, 'default' => 50 ], 'offset' => [ 'type' => 'integer', 'minimum' => 0, 'default' => 0 ] ], 'additionalProperties' => false ],
59 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'forms' => [ 'type' => 'array' ], 'total' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
60 'execute_callback' => function( $input = [] ) {
61 $qa = [ 'post_type' => 'wpcf7_contact_form', 'post_status' => 'any', 'posts_per_page' => isset( $input['limit'] ) ? max( 1, (int) $input['limit'] ) : 50, 'offset' => isset( $input['offset'] ) ? max( 0, (int) $input['offset'] ) : 0, 'orderby' => 'date', 'order' => 'DESC' ];
62 if ( isset( $input['search'] ) && $input['search'] !== '' ) { $qa['s'] = (string) $input['search']; }
63 $q = new WP_Query( $qa );
64 $out = [];
65 foreach ( $q->posts as $post ) { $out[] = AVCF_CF7_Helpers::map_form_summary( $post ); }
66 return [ 'success' => true, 'forms' => $out, 'total' => (int) $q->found_posts, 'message' => sprintf( '%d form(s). For submission data, use the atarim/flamingo-* abilities.', count( $out ) ) ];
67 },
68 'permission_callback' => function() use ( $self ) { return $self->can_read(); },
69 'meta' => $this->ro_meta(),
70 ] );
71 }
72
73 /* ----------------------------- get-form ---------------------------- */
74
75 private function register_get_form() {
76 $self = $this;
77 wp_register_ability( 'atarim/cf7-get-form', [
78 'label' => 'Get Contact Form 7 Form', 'category' => 'atarim',
79 'description' => 'CF7 form overview: fields parsed from the form template (id, type, required), mail / mail_2 notification summaries, and shortcode.',
80 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
81 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'form' => [ 'type' => 'object' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
82 'execute_callback' => function( $input = [] ) {
83 $post = get_post( (int) $input['form_id'] );
84 if ( ! $post || $post->post_type !== 'wpcf7_contact_form' ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
85 return [ 'success' => true, 'form' => AVCF_CF7_Helpers::map_form_full( $post, AVCF_CF7_Helpers::get_cf7( (int) $input['form_id'] ) ), 'message' => 'OK.' ];
86 },
87 'permission_callback' => function() use ( $self ) { return $self->can_read(); },
88 'meta' => $this->ro_meta(),
89 ] );
90 }
91
92 /* ------------------------- get-form-template ----------------------- */
93
94 private function register_get_form_template() {
95 $self = $this;
96 wp_register_ability( 'atarim/cf7-get-form-template', [
97 'label' => 'Get CF7 Form Template', 'category' => 'atarim',
98 'description' => 'The raw [form] template markup (CF7 form-tags) for a form.',
99 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
100 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'template' => [ 'type' => 'string' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
101 'execute_callback' => function( $input = [] ) {
102 $cf7 = AVCF_CF7_Helpers::get_cf7( (int) $input['form_id'] );
103 if ( ! $cf7 || ! method_exists( $cf7, 'prop' ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
104 return [ 'success' => true, 'template' => (string) $cf7->prop( 'form' ), 'message' => 'OK.' ];
105 },
106 'permission_callback' => function() use ( $self ) { return $self->can_read(); },
107 'meta' => $this->ro_meta(),
108 ] );
109 }
110
111 /* ------------------------ list-mail-templates ---------------------- */
112
113 private function register_list_mail_templates() {
114 $self = $this;
115 wp_register_ability( 'atarim/cf7-list-mail-templates', [
116 'label' => 'List CF7 Mail Templates', 'category' => 'atarim',
117 'description' => 'The full mail and mail_2 templates: recipient, sender, subject, body, additional_headers, use_html, active.',
118 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
119 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'mail_templates' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
120 'execute_callback' => function( $input = [] ) {
121 $cf7 = AVCF_CF7_Helpers::get_cf7( (int) $input['form_id'] );
122 if ( ! $cf7 || ! method_exists( $cf7, 'prop' ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
123 $out = [];
124 foreach ( [ 'mail', 'mail_2' ] as $key ) {
125 $m = AVCF_CF7_Helpers::map_mail( $cf7->prop( $key ), $key );
126 if ( $m ) { $out[] = $m; }
127 }
128 return [ 'success' => true, 'mail_templates' => $out, 'message' => sprintf( '%d mail template(s).', count( $out ) ) ];
129 },
130 'permission_callback' => function() use ( $self ) { return $self->can_read(); },
131 'meta' => $this->ro_meta(),
132 ] );
133 }
134
135 /* ----------------------------- get-messages ------------------------ */
136
137 private function register_get_messages() {
138 $self = $this;
139 wp_register_ability( 'atarim/cf7-get-messages', [
140 'label' => 'Get CF7 Messages', 'category' => 'atarim',
141 'description' => 'The form\'s validation/status messages (mail sent, validation errors, spam, etc.) as a key => text map.',
142 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
143 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'messages' => [ 'type' => 'object' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
144 'execute_callback' => function( $input = [] ) {
145 $cf7 = AVCF_CF7_Helpers::get_cf7( (int) $input['form_id'] );
146 if ( ! $cf7 || ! method_exists( $cf7, 'prop' ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
147 $messages = $cf7->prop( 'messages' );
148 return [ 'success' => true, 'messages' => is_array( $messages ) ? $messages : [], 'message' => 'OK.' ];
149 },
150 'permission_callback' => function() use ( $self ) { return $self->can_read(); },
151 'meta' => $this->ro_meta(),
152 ] );
153 }
154
155 /* ---------------------- get-additional-settings -------------------- */
156
157 private function register_get_additional_settings() {
158 $self = $this;
159 wp_register_ability( 'atarim/cf7-get-additional-settings', [
160 'label' => 'Get CF7 Additional Settings', 'category' => 'atarim',
161 'description' => 'The "Additional Settings" text block of a form (raw lines, e.g. demo_mode, skip_mail, on_sent_ok).',
162 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
163 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'additional_settings' => [ 'type' => 'string' ], 'lines' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
164 'execute_callback' => function( $input = [] ) {
165 $cf7 = AVCF_CF7_Helpers::get_cf7( (int) $input['form_id'] );
166 if ( ! $cf7 || ! method_exists( $cf7, 'prop' ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
167 $text = (string) $cf7->prop( 'additional_settings' );
168 $lines = array_values( array_filter( array_map( 'trim', preg_split( '/\r\n|\r|\n/', $text ) ) ) );
169 return [ 'success' => true, 'additional_settings' => $text, 'lines' => $lines, 'message' => 'OK.' ];
170 },
171 'permission_callback' => function() use ( $self ) { return $self->can_read(); },
172 'meta' => $this->ro_meta(),
173 ] );
174 }
175
176 /* ------------------------ update-notifications --------------------- */
177
178 private function register_update_notifications() {
179 $self = $this;
180 wp_register_ability( 'atarim/cf7-update-notifications', [
181 'label' => 'Update CF7 Notification Recipients', 'category' => 'atarim',
182 'description' => 'Update the recipient of the mail and/or mail_2 templates. updates: [{ notification_id: "mail"|"mail_2", recipients: [emails] }]. Emails sanitized; unknown/unconfigured templates skipped.',
183 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'updates' => [ 'type' => 'array', 'items' => [ 'type' => 'object' ], 'minItems' => 1 ] ], 'required' => [ 'form_id', 'updates' ], 'additionalProperties' => false ],
184 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'updated' => [ 'type' => 'array' ], 'skipped' => [ 'type' => 'array' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
185 'execute_callback' => function( $input = [] ) {
186 $cf7 = AVCF_CF7_Helpers::get_cf7( (int) $input['form_id'] );
187 if ( ! $cf7 || ! method_exists( $cf7, 'prop' ) ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
188 $updated = []; $skipped = [];
189 foreach ( (array) $input['updates'] as $u ) {
190 $nid = isset( $u['notification_id'] ) ? (string) $u['notification_id'] : '';
191 if ( ! in_array( $nid, [ 'mail', 'mail_2' ], true ) ) { $skipped[] = [ 'notification_id' => $nid, 'reason' => 'CF7 supports "mail" and "mail_2" only.' ]; continue; }
192 $mail = $cf7->prop( $nid );
193 if ( ! is_array( $mail ) ) { $skipped[] = [ 'notification_id' => $nid, 'reason' => 'Not configured on this form.' ]; continue; }
194 $clean = array_filter( array_map( 'sanitize_email', isset( $u['recipients'] ) ? (array) $u['recipients'] : [] ) );
195 if ( empty( $clean ) ) { $skipped[] = [ 'notification_id' => $nid, 'reason' => 'No valid recipient emails.' ]; continue; }
196 $mail['recipient'] = implode( ', ', $clean );
197 $cf7->set_properties( [ $nid => $mail ] );
198 $updated[] = $nid;
199 }
200 if ( ! empty( $updated ) && method_exists( $cf7, 'save' ) ) { $cf7->save(); }
201 return [ 'success' => ! empty( $updated ), 'updated' => $updated, 'skipped' => $skipped, 'message' => sprintf( '%d updated, %d skipped.', count( $updated ), count( $skipped ) ) ];
202 },
203 'permission_callback' => function() use ( $self ) { return $self->can_edit(); },
204 'meta' => $this->write_meta( false ),
205 ] );
206 }
207
208 /* -------------------------- duplicate-form ------------------------- */
209
210 private function register_duplicate_form() {
211 $self = $this;
212 wp_register_ability( 'atarim/cf7-duplicate-form', [
213 'label' => 'Duplicate CF7 Form', 'category' => 'atarim',
214 'description' => 'Clone a form via the CF7 model copy(). Optional new_title (defaults to "<title> (Copy)").',
215 'input_schema' => [ 'type' => 'object', 'properties' => [ 'form_id' => [ 'type' => 'integer', 'minimum' => 1 ], 'new_title' => [ 'type' => 'string' ] ], 'required' => [ 'form_id' ], 'additionalProperties' => false ],
216 'output_schema'=> [ 'type' => 'object', 'properties' => [ 'success' => [ 'type' => 'boolean' ], 'new_form_id' => [ 'type' => 'integer' ], 'message' => [ 'type' => 'string' ] ], 'required' => [ 'success', 'message' ] ],
217 'execute_callback' => function( $input = [] ) {
218 $orig = AVCF_CF7_Helpers::get_cf7( (int) $input['form_id'] );
219 if ( ! $orig ) { return [ 'success' => false, 'message' => 'Form not found.' ]; }
220 if ( ! method_exists( $orig, 'copy' ) ) { return [ 'success' => false, 'message' => 'CF7 copy() unavailable in this version.' ]; }
221 $title = isset( $input['new_title'] ) && $input['new_title'] !== '' ? sanitize_text_field( $input['new_title'] ) : ( (string) $orig->title() . ' (Copy)' );
222 $copy = $orig->copy();
223 if ( $copy && method_exists( $copy, 'set_title' ) ) { $copy->set_title( $title ); }
224 if ( ! $copy || ! method_exists( $copy, 'save' ) ) { return [ 'success' => false, 'message' => 'Duplicate failed.' ]; }
225 $copy->save();
226 return [ 'success' => true, 'new_form_id' => (int) $copy->id(), 'message' => sprintf( 'Duplicated as "%s".', $title ) ];
227 },
228 'permission_callback' => function() use ( $self ) { return $self->can_edit(); },
229 'meta' => $this->write_meta( false ),
230 ] );
231 }
232 }
233