← 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); |