PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.7.1
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.7.1
2.7.1 2.7.0 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.4.0 2.3.3 2.3.2 2.3.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 All 130 releases
← All changes | includes/abilities/widgets/wdesignkit-update-widget.php +107 -4 2.5.4 → 2.7.1 View file →
@@ -9,12 +9,12 @@
9 9 exit();
10 10 }
11 11
12 12 wp_register_ability('wdesignkit/update-widget', [
13 - 'label' => __('Update WDesignKit Widget', 'sprout-mcp'),
13 + 'label' => __('Update WDesignKit Widget', 'wdesignkit'),
14 14 'description' => __(
15 15 'Updates an existing widget\'s PHP code, CSS styles, JS scripts, or JSON config metadata. Each code field is a FULL file replacement. Always use get-widget first to read the current code, then provide the complete updated content.',
16 - 'sprout-mcp',
16 + 'wdesignkit',
17 17 ),
18 18 'category' => 'wdesignkit',
19 19 'input_schema' => [
20 20 'type' => 'object',
@@ -61,8 +61,12 @@
61 61 'description' => 'Optional documentation URL for the widget. Updates widgetdata.helper_link in the JSON config. Pass an empty string to clear it.',
62 62 'format' => 'uri',
63 63 'maxLength' => 512,
64 64 ],
65 + 'validate' => [
66 + 'type' => 'boolean',
67 + 'description' => 'When true, runs wdesignkit/validate-widget inline first and refuses the update if validation checks fail.',
68 + ],
65 69 ],
66 70 'required' => ['builder', 'folder'],
67 71 'additionalProperties' => false,
68 72 ],
@@ -76,10 +80,11 @@
76 80 'widget_id' => ['type' => 'string'],
77 81 ],
78 82 ],
79 83 'execute_callback' => 'wdesignkit_mcp_update_widget',
80 - 'permission_callback' => 'sprout_mcp_permission_callback',
84 + 'permission_callback' => 'wdesignkit_mcp_permission_callback',
81 85 'meta' => [
86 + 'public' => true,
82 87 'show_in_rest' => true,
83 88 'mcp' => ['public' => true],
84 89 'annotations' => [
85 90 'instructions' => implode("\n", [
@@ -112,8 +117,9 @@
112 117 '### Workflow',
113 118 '1. wdesignkit/list-widgets → find builder + folder',
114 119 '2. wdesignkit/get-widget → read current code',
115 120 '3. wdesignkit/update-widget → provide full updated file content',
121 + '4. wdesignkit/sync-widget-code → run after raw php_code updates to keep section_data in sync',
116 122 ]),
117 123 'readonly' => false,
118 124 'destructive' => true,
119 125 'idempotent' => true,
@@ -198,8 +204,35 @@
198 204 if (!empty($input['version']) && !preg_match('/^\d+\.\d+\.\d+$/', $input['version'])) {
199 205 return ['success' => false, 'message' => 'Invalid version format. Use semver like "1.2.0".'];
200 206 }
201 207
208 + // --- Pre-write code validation if requested ---
209 + if (!empty($input['validate'])) {
210 + if (!function_exists('wdesignkit_mcp_validate_widget')) {
211 + $vw_path = __DIR__ . '/wdesignkit-validate-widget.php';
212 + if (file_exists($vw_path)) {
213 + require_once $vw_path;
214 + }
215 + }
216 + if (function_exists('wdesignkit_mcp_validate_widget')) {
217 + $val_res = wdesignkit_mcp_validate_widget([
218 + 'php_code' => $input['php_code'] ?? '',
219 + 'css_code' => $input['css_code'] ?? '',
220 + 'js_code' => $input['js_code'] ?? '',
221 + 'builder' => $builder,
222 + 'folder' => $folder,
223 + 'name' => $new_name ?: $folder,
224 + ]);
225 + if (empty($val_res['valid'])) {
226 + return [
227 + 'success' => false,
228 + 'message' => 'Validation failed: ' . ($val_res['message'] ?? 'Widget code failed validation checks.'),
229 + 'checks' => $val_res['checks'] ?? [],
230 + ];
231 + }
232 + }
233 + }
234 +
202 235 // --- Tracking arrays ---
203 236 $files_updated = [];
204 237 $write_errors = [];
205 238
@@ -216,10 +249,28 @@
216 249 }
217 250 };
218 251
219 252 // --- Update code files (only fields explicitly provided) ---
253 + // Site-level opt-out for generated widget PHP (ClickUp 86d41zavc). Gating create-widget alone
254 + // would be theatre: an operator blocked from creating a widget could simply overwrite an
255 + // existing widget's PHP here instead — same capability, same sink, same result. Scoped to the
256 + // php_code branch on purpose, so a locked-down site can still update css/js/json.
257 + if (isset($input['php_code']) && $input['php_code'] !== ''
258 + && function_exists('wdesignkit_widget_php_write_allowed') && !wdesignkit_widget_php_write_allowed()) {
259 + return [
260 + 'success' => false,
261 + 'message' => 'php_code was not written: generated widget PHP writes are disabled on this site via the wdesignkit_allow_widget_php_write filter. Re-send without php_code to update css_code / js_code / metadata only.',
262 + ];
263 + }
264 +
220 265 if (isset($input['php_code']) && $input['php_code'] !== '') {
221 - $maybe_write($file_base . '.php', $input['php_code']);
266 + // Strip any namespace declaration — the widget loader instantiates the bare
267 + // global class name, so namespaced PHP would fatal with "Class not found".
268 + // (create-widget applies the same guard; see wdesignkit_mcp_strip_php_namespace.)
269 + $php_for_write = function_exists('wdesignkit_mcp_strip_php_namespace')
270 + ? wdesignkit_mcp_strip_php_namespace((string) $input['php_code'])
271 + : (string) $input['php_code'];
272 + $maybe_write($file_base . '.php', $php_for_write);
222 273 }
223 274
224 275 if (isset($input['css_code']) && $input['css_code'] !== '') {
225 276 $maybe_write($file_base . '.css', $input['css_code']);
@@ -276,8 +327,60 @@
276 327 }
277 328 if (!empty($input['version'])) {
278 329 $json_data['widget_data']['widgetdata']['widget_version'] = sanitize_text_field($input['version']);
279 330 $meta_changed = true;
331 + }
332 +
333 + // --- Resync builder panel (section_data) + Editor_data when code changes ---
334 + // The WDesignKit builder panel reads section_data / Editor_data, NOT the raw code
335 + // files — so updating a widget's code otherwise leaves the panel showing the old
336 + // controls and preview. Mirror create-widget: re-parse register_controls() from the
337 + // new PHP into section_data, regenerate the canvas HTML from it, and keep the preview
338 + // CSS/JS in step with the written files.
339 + $code_touched = (isset($input['php_code']) && $input['php_code'] !== '')
340 + || (isset($input['css_code']) && $input['css_code'] !== '')
341 + || (isset($input['js_code']) && $input['js_code'] !== '');
342 +
343 + if ($code_touched) {
344 + if (!isset($json_data['Editor_data']) || !is_array($json_data['Editor_data'])) {
345 + $json_data['Editor_data'] = ['html' => '', 'css' => '', 'js' => ''];
346 + }
347 +
348 + // section_data + canvas HTML — only when new PHP is supplied and it parses.
349 + // A null parse (e.g. non-Elementor register_controls) leaves the existing
350 + // section_data untouched rather than clobbering it with defaults.
351 + if (isset($input['php_code']) && $input['php_code'] !== ''
352 + && !isset($input['section_data'])
353 + && function_exists('wdesignkit_mcp_parse_php_section_data')) {
354 +
355 + $wd_name = (string) ($json_data['widget_data']['widgetdata']['name'] ?? $folder);
356 + $wd_id = (string) ($json_data['widget_data']['widgetdata']['widget_id'] ?? '');
357 + $file_name = basename($file_base);
358 +
359 + $_slug = sanitize_title($wd_name);
360 + $widget_css_class = 'wdkit-' . $_slug . (substr($_slug, -7) === '-widget' ? '' : '-widget');
361 +
362 + $parsed = wdesignkit_mcp_parse_php_section_data((string) $input['php_code'], $widget_css_class);
363 + if (is_array($parsed)) {
364 + $json_data['section_data'] = $parsed;
365 + $meta_changed = true;
366 +
367 + if (function_exists('wdesignkit_mcp_generate_editor_html_from_section_data')) {
368 + $json_data['Editor_data']['html'] = wdesignkit_mcp_generate_editor_html_from_section_data(
369 + $wd_id, $widget_css_class, $file_name, $parsed
370 + );
371 + }
372 + }
373 + }
374 +
375 + if (isset($input['css_code']) && $input['css_code'] !== '') {
376 + $json_data['Editor_data']['css'] = (string) $input['css_code'];
377 + $meta_changed = true;
378 + }
379 + if (isset($input['js_code']) && $input['js_code'] !== '') {
380 + $json_data['Editor_data']['js'] = (string) $input['js_code'];
381 + $meta_changed = true;
382 + }
280 383 }
281 384
282 385 if ($meta_changed) {
283 386 $new_json_content = wp_json_encode($json_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);