PluginProbe
The Innovative Form Builder – IvyForms / 1.5
The Innovative Form Builder – IvyForms v1.5
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Common / Helpers / McpHelpers / McpAbilitiesHelper.php

McpAbilitiesHelper.php in The Innovative Form Builder – IvyForms 1.5, at backend/src/Common/Helpers/McpHelpers/McpAbilitiesHelper.php

236 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @copyright © Melograno Venture Studio. All rights reserved.
5 * @licence See COPYING.md for license details.
6 */
7
8 namespace IvyForms\Common\Helpers\McpHelpers;
9
10 use IvyForms\Infrastructure\WP\MCP\McpAbilityPermissions;
11 use IvyForms\Infrastructure\WP\MCP\McpFeatureGate;
12 use WP_REST_Request;
13
14 /**
15 * Shared helpers used by IvyForms MCP ability registrars.
16 */
17 class McpAbilitiesHelper
18 {
19 private const NAMESPACE = 'ivyforms/v1';
20
21 /**
22 * Executes a REST request to the IvyForms internal REST API on behalf of an
23 * MCP ability execution.
24 *
25 * The MCP boundary already enforces user/capability checks; we add a fail-closed
26 * guard here so that a future bug in a registrar (or a third-party caller) cannot
27 * bypass authentication and reach the admin REST controllers directly.
28 *
29 * @param array<string, mixed> $params
30 *
31 * @return array<int|string, mixed>|int|string|bool|float|null
32 */
33 public static function restRequest(string $method, string $route, array $params = [])
34 {
35 if (!McpFeatureGate::isEnabled() || !McpAbilityPermissions::canUseMcp()) {
36 return [
37 'code' => 'rest_forbidden',
38 'message' => 'Insufficient permission for IvyForms MCP REST bridge.',
39 'data' => ['status' => 403],
40 ];
41 }
42
43 $request = new WP_REST_Request($method, '/' . self::NAMESPACE . $route);
44 $request->set_header('X-WP-Nonce', wp_create_nonce('wp_rest'));
45
46 if ($method === 'GET') {
47 $request->set_query_params($params);
48 }
49
50 if ($method !== 'GET') {
51 // Populate both JSON body and body params so controllers using
52 // get_param()/get_params() work whether WordPress parses JSON or not.
53 $request->set_header('Content-Type', 'application/json');
54 $request->set_body(wp_json_encode($params));
55 $request->set_body_params($params);
56 }
57
58 $response = rest_do_request($request);
59 $data = rest_get_server()->response_to_data($response, false);
60
61 return $data;
62 }
63
64 /**
65 * Extracts the inner IvyForms REST payload portion from a full REST response.
66 *
67 * @param mixed $response REST payload
68 *
69 * @return array<int|string, mixed>|int|string|bool|float|null
70 */
71 public static function extractIvyFormsRestPayload($response)
72 {
73 if (!is_array($response)) {
74 return $response;
75 }
76
77 return $response['data']['data'] ?? $response['data'] ?? $response;
78 }
79
80 /**
81 * @param mixed $response REST payload
82 *
83 * @return array<int|string, mixed>
84 */
85 public static function notificationArrayFromRestResponse($response): array
86 {
87 $payload = self::extractIvyFormsRestPayload($response);
88
89 if (!is_array($payload)) {
90 return [];
91 }
92
93 if (
94 isset($payload['message'], $payload['data'])
95 && is_array($payload['data'])
96 && array_key_exists('id', $payload['data'])
97 ) {
98 return $payload['data'];
99 }
100
101 return $payload;
102 }
103
104 /**
105 * Builds a partial update payload for notification settings.
106 *
107 * @param array<string, mixed> $notification
108 * @return array<string, mixed>
109 */
110 public static function buildNotificationUpdatePayload(array $notification): array
111 {
112 $keys = [
113 'id',
114 'name',
115 'sender',
116 'replyTo',
117 'receiver',
118 'cc',
119 'bcc',
120 'enabled',
121 'subject',
122 'message',
123 'showEmptyFields',
124 'smartLogic',
125 'formId',
126 ];
127
128 $payload = [];
129
130 foreach ($keys as $key) {
131 if (array_key_exists($key, $notification)) {
132 $payload[$key] = $notification[$key];
133 }
134 }
135
136 return $payload;
137 }
138
139 private static function formNameForSubject(int $formId): string
140 {
141 if ($formId <= 0) {
142 return '';
143 }
144
145 $response = self::restRequest('GET', '/form/' . $formId);
146 $form = self::extractIvyFormsRestPayload($response);
147
148 if (
149 isset($form['message'], $form['data'])
150 && is_array($form['data'])
151 && array_key_exists('name', $form['data'])
152 ) {
153 $form = $form['data'];
154 }
155
156 if (!is_array($form) || empty($form['name'])) {
157 return '';
158 }
159
160 return sanitize_text_field((string) $form['name']);
161 }
162
163 public static function notificationSubjectForForm(int $formId, string $subject = ''): string
164 {
165 $formName = self::formNameForSubject($formId);
166
167 if ($subject === '') {
168 return $formName
169 ? sprintf('New form submission: %s', $formName)
170 : 'New form submission';
171 }
172
173 if ($formName === '') {
174 return $subject;
175 }
176
177 return str_replace(['{form_name}', '{{form_name}}'], $formName, $subject);
178 }
179
180 /**
181 * @param mixed $response REST payload returned by {@see restRequest()}.
182 */
183 public static function isRestMutationSuccessful($response): bool
184 {
185 if (!is_array($response)) {
186 return false;
187 }
188
189 if (isset($response['code']) && is_string($response['code']) && $response['code'] !== '') {
190 return false;
191 }
192
193 $status = (int) ($response['data']['status'] ?? 200);
194
195 return $status >= 200 && $status < 300;
196 }
197
198 /**
199 * @param mixed $response REST payload returned by {@see restRequest()}.
200 */
201 public static function restMutationErrorMessage($response, string $fallback): string
202 {
203 if (!is_array($response)) {
204 return $fallback;
205 }
206
207 $message = $response['message'] ?? null;
208 if (is_string($message) && $message !== '') {
209 return $message;
210 }
211
212 $nestedMessage = $response['data']['message'] ?? null;
213 if (is_string($nestedMessage) && $nestedMessage !== '') {
214 return $nestedMessage;
215 }
216
217 return $fallback;
218 }
219
220 public static function adminFormUrl(int $formId): string
221 {
222 if ($formId <= 0) {
223 return '';
224 }
225 return admin_url('admin.php?page=ivyforms-builder#/manage/' . $formId);
226 }
227
228 public static function adminEntryUrl(int $entryId): string
229 {
230 if ($entryId <= 0) {
231 return '';
232 }
233 return admin_url('admin.php?page=ivyforms-entries#/details/' . $entryId);
234 }
235 }
236