← All changes
|
includes/abilities/widgets/wdesignkit-create-widget.php
+264
-31
2.5.0
→
2.7.0
View file →
| @@ -9,12 +9,12 @@ | ||
| 9 | 9 | exit(); |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | 12 | wp_register_ability('wdesignkit/create-widget', [ |
| 13 | - 'label' => __('Create WDesignKit Widget', 'sprout-mcp'), | |
| 13 | + 'label' => __('Create WDesignKit Widget', 'wdesignkit'), | |
| 14 | 14 | 'description' => __( |
| 15 | 15 | 'Creates a new widget for Elementor, Gutenberg, Gutenberg Core, or Bricks builder. Generates all required files (PHP, JSON config, CSS, JS) with proper boilerplate code. You can provide custom code for each file or use defaults.', |
| 16 | - 'sprout-mcp', | |
| 16 | + 'wdesignkit', | |
| 17 | 17 | ), |
| 18 | 18 | 'category' => 'wdesignkit', |
| 19 | 19 | 'input_schema' => [ |
| 20 | 20 | 'type' => 'object', |
| @@ -26,9 +26,9 @@ | ||
| 26 | 26 | 'maxLength' => 64, |
| 27 | 27 | ], |
| 28 | 28 | 'builder' => [ |
| 29 | 29 | 'type' => 'string', |
| 30 | - 'description' => 'Target page builder.', | |
| 30 | + 'description' => 'Target page builder. Use "gutenberg" for Nexter Blocks / The Plus Addons for Block Editor (TPGB) — "Nexter", "Nexter Blocks", "TPGB", and "Plus Block Editor" all map to builder=gutenberg; it builds a block on the Pmgc component framework. Use "gutenberg_core" for a plain native WordPress block with no TPGB dependency. Note: "Nexter Extension" is a separate plugin, not a builder.', | |
| 31 | 31 | 'enum' => ['elementor', 'gutenberg', 'gutenberg_core', 'bricks'], |
| 32 | 32 | ], |
| 33 | 33 | 'description' => [ |
| 34 | 34 | 'type' => 'string', |
| @@ -117,10 +117,11 @@ | ||
| 117 | 117 | ], |
| 118 | 118 | ], |
| 119 | 119 | ], |
| 120 | 120 | 'execute_callback' => 'wdesignkit_mcp_create_widget', |
| 121 | - 'permission_callback' => 'sprout_mcp_permission_callback', | |
| 121 | + 'permission_callback' => 'wdesignkit_mcp_permission_callback', | |
| 122 | 122 | 'meta' => [ |
| 123 | + 'public' => true, | |
| 123 | 124 | 'show_in_rest' => true, |
| 124 | 125 | 'mcp' => ['public' => true], |
| 125 | 126 | 'annotations' => [ |
| 126 | 127 | 'instructions' => implode("\n", [ |
| @@ -146,8 +147,12 @@ | ||
| 146 | 147 | '### Required inputs', |
| 147 | 148 | '- name: 1–64 chars. Letters, numbers, spaces, hyphens, underscores ONLY.', |
| 148 | 149 | ' Special characters are REJECTED with an error — do not pass them. Names must be unique per builder.', |
| 149 | 150 | '- builder: one of elementor | gutenberg | gutenberg_core | bricks', |
| 151 | + ' Builder aliases — "Nexter" / "Nexter Blocks" / "TPGB" / "The Plus Addons for Block Editor" / "Plus Block Editor" all mean builder=gutenberg.', | |
| 152 | + ' gutenberg = Nexter Blocks (TPGB): block built on the Pmgc component framework', | |
| 153 | + ' (Pmgc_PanelTabs/Pmgc_Tab inspector, Pmgc_CssGenerator styling), loaded from the gutenberg/ folder.', | |
| 154 | + ' "gutenberg_core" = plain native WordPress blocks (no TPGB framework). "Nexter Extension" is a SEPARATE plugin, NOT a builder — do not treat it as the target builder.', | |
| 150 | 155 | '', |
| 151 | 156 | '### Code inputs (php_code, css_code, js_code, cdn_js, cdn_css, section_data, editor_html)', |
| 152 | 157 | '- php_code / css_code / js_code: COMPLETE file replacements, not partial snippets.', |
| 153 | 158 | '- STRONGLY RECOMMENDED: omit php_code and let the ability generate correct boilerplate.', |
| @@ -237,8 +242,18 @@ | ||
| 237 | 242 | '### After creation', |
| 238 | 243 | '- Use wdesignkit/get-widget to read back the generated files.', |
| 239 | 244 | '- Use wdesignkit/update-widget to modify code.', |
| 240 | 245 | '- Does NOT require WDesignKit cloud login.', |
| 246 | + '', | |
| 247 | + '### ⚠ Trust boundary — this ability executes PHP', | |
| 248 | + 'The php_code written here is saved to disk and require_once\'d by the builder', | |
| 249 | + 'loaders on subsequent requests, so calling this ability runs arbitrary PHP as the', | |
| 250 | + 'web server user. That is what a widget builder is, and it is gated on', | |
| 251 | + 'manage_options — the same capability WordPress requires for the plugin editor.', | |
| 252 | + 'It does mean this ability, create-widget and update-widget, must never be exposed', | |
| 253 | + 'to any automation context trusted below a full site administrator. Treat php_code', | |
| 254 | + 'from an untrusted source exactly as you would treat pasting it into wp-admin.', | |
| 255 | + '(ClickUp 86d41zavc)', | |
| 241 | 256 | ]), |
| 242 | 257 | 'readonly' => false, |
| 243 | 258 | 'destructive' => false, |
| 244 | 259 | 'idempotent' => false, |
| @@ -253,8 +268,20 @@ | ||
| 253 | 268 | 'message' => 'WDesignKit plugin is not active.', |
| 254 | 269 | ]; |
| 255 | 270 | } |
| 256 | 271 | |
| 272 | + // Site-level opt-out for generated widget PHP (ClickUp 86d41zavc). Defaults to allowed, so | |
| 273 | + // this changes nothing unless an operator explicitly filters it off: | |
| 274 | + // add_filter( 'wdesignkit_allow_widget_php_write', '__return_false' ); | |
| 275 | + // See wdesignkit_widget_php_write_allowed() for why this is a dedicated filter rather than a | |
| 276 | + // DISALLOW_FILE_MODS / DISALLOW_FILE_EDIT check. | |
| 277 | + if (function_exists('wdesignkit_widget_php_write_allowed') && !wdesignkit_widget_php_write_allowed()) { | |
| 278 | + return [ | |
| 279 | + 'success' => false, | |
| 280 | + 'message' => 'Widget creation is disabled on this site: generated widget PHP writes have been turned off via the wdesignkit_allow_widget_php_write filter.', | |
| 281 | + ]; | |
| 282 | + } | |
| 283 | + | |
| 257 | 284 | // --- Validate name --- |
| 258 | 285 | $raw_name = $input['name'] ?? ''; |
| 259 | 286 | if (!is_string($raw_name) || trim($raw_name) === '') { |
| 260 | 287 | return ['success' => false, 'message' => 'Widget name is required.']; |
| @@ -337,19 +364,16 @@ | ||
| 337 | 364 | return ['success' => false, 'message' => 'Invalid version format. Use semver format like "1.0.0".']; |
| 338 | 365 | } |
| 339 | 366 | |
| 340 | 367 | // --- Generate folder and file names matching WDesignKit convention --- |
| 341 | - // folder: kebab-case (e.g. "my-custom-card") | |
| 342 | - // file: snake_case (e.g. "my_custom_card") | |
| 343 | - // Normalize ALL separators: folder is pure kebab-case, file is pure snake_case. | |
| 344 | - // Without this, a name like "My-Custom Widget" produces "my-custom_widget" (mixed). | |
| 345 | - $folder_base = sanitize_file_name(strtolower(str_replace([' ', '_'], '-', $name))); | |
| 346 | - $file_base = sanitize_file_name(strtolower(str_replace([' ', '-'], '_', $name))); | |
| 347 | - | |
| 368 | + // folder: "My-Custom-Card_<hash>" file: "My_Custom_Card_<hash>" | |
| 369 | + // Case is PRESERVED. This used to lowercase both, which put widgets created here in a | |
| 370 | + // different folder than the one the download/import/save paths write — the same widget | |
| 371 | + // ended up in two directories differing only by case (ClickUp 86d3yk4yx). | |
| 348 | 372 | // 8-character unique hex hash — matches the real WDesignKit widget format (e.g. "kmrd6l24") |
| 349 | 373 | $widget_hash = substr(md5(uniqid('wdkit', true)), 0, 8); |
| 350 | - $folder_name_full = $folder_base . '_' . $widget_hash; | |
| 351 | - $file_name_full = $file_base . '_' . $widget_hash; | |
| 374 | + $folder_name_full = wdesignkit_widget_folder_name($name, $widget_hash); | |
| 375 | + $file_name_full = wdesignkit_widget_file_name($name, $widget_hash); | |
| 352 | 376 | |
| 353 | 377 | // widget_id = JUST the hash (matches the real widget JSON format: "widget_id":"20hzyv26") |
| 354 | 378 | $widget_id = $widget_hash; |
| 355 | 379 | |
| @@ -422,14 +446,23 @@ | ||
| 422 | 446 | $php_code = wdesignkit_mcp_generate_php( |
| 423 | 447 | $builder, $class_name, $name, $folder_name_full, $file_name_full, |
| 424 | 448 | $description, $category, $widget_hash, $version |
| 425 | 449 | ); |
| 426 | - } elseif ($builder === 'elementor') { | |
| 427 | - // Auto-correct common mistakes in AI-provided Elementor PHP: | |
| 428 | - // wrong class name, wrong get_name() return value, missing asset-enqueue methods. | |
| 429 | - $php_code = wdesignkit_mcp_fix_elementor_php( | |
| 430 | - $php_code, $file_name_full, $widget_hash, $folder_name_full, $version | |
| 431 | - ); | |
| 450 | + } else { | |
| 451 | + // Caller-supplied PHP must live in the global namespace. Every builder's widget | |
| 452 | + // loader instantiates/registers the class by its bare global name (e.g. the | |
| 453 | + // Elementor loader calls `new Wdkit_{file}()`), so a `namespace ...;` declaration | |
| 454 | + // makes the class resolve as Namespace\Wdkit_xxx and the loader fatals with | |
| 455 | + // "Class not found", crashing every editor/frontend load. Strip it before writing. | |
| 456 | + $php_code = wdesignkit_mcp_strip_php_namespace($php_code); | |
| 457 | + | |
| 458 | + if ($builder === 'elementor') { | |
| 459 | + // Auto-correct common mistakes in AI-provided Elementor PHP: | |
| 460 | + // wrong class name, wrong get_name() return value, missing asset-enqueue methods. | |
| 461 | + $php_code = wdesignkit_mcp_fix_elementor_php( | |
| 462 | + $php_code, $file_name_full, $widget_hash, $folder_name_full, $version | |
| 463 | + ); | |
| 464 | + } | |
| 432 | 465 | } |
| 433 | 466 | |
| 434 | 467 | $js_code = (string) ($input['js_code'] ?? ''); |
| 435 | 468 | if (empty($js_code)) { |
| @@ -519,8 +552,22 @@ | ||
| 519 | 552 | if (file_put_contents($widget_file_base . '.css', $css_code) === false) { |
| 520 | 553 | $write_errors[] = 'CSS'; |
| 521 | 554 | } |
| 522 | 555 | |
| 556 | + // Thumbnail: create-widget has no image input and the JSON's w_image points at | |
| 557 | + // {file}.png, so without this every new widget shows a broken image in the library | |
| 558 | + // AND push-widget's pre-flight (which scans the folder for a jpg/png/webp) blocks the | |
| 559 | + // push. Seed a real default thumbnail from the bundled placeholder so both work; the | |
| 560 | + // caller can overwrite {file}.png later with a custom image. Best-effort — never fail | |
| 561 | + // widget creation over the thumbnail. (bug: 86d3rc7n8) | |
| 562 | + $thumb_written = false; | |
| 563 | + if (defined('WDKIT_PATH')) { | |
| 564 | + $default_thumb = WDKIT_PATH . 'assets/images/jpg/placeholder.png'; | |
| 565 | + if (is_readable($default_thumb)) { | |
| 566 | + $thumb_written = @copy($default_thumb, $widget_file_base . '.png'); | |
| 567 | + } | |
| 568 | + } | |
| 569 | + | |
| 523 | 570 | if (!empty($write_errors)) { |
| 524 | 571 | return [ |
| 525 | 572 | 'success' => false, |
| 526 | 573 | 'message' => 'Failed to write files: ' . implode(', ', $write_errors) . '. Check directory permissions.', |
| @@ -526,8 +573,22 @@ | ||
| 526 | 573 | 'message' => 'Failed to write files: ' . implode(', ', $write_errors) . '. Check directory permissions.', |
| 527 | 574 | ]; |
| 528 | 575 | } |
| 529 | 576 | |
| 577 | + if (function_exists('wdesignkit_invalidate_widget_registry')) { | |
| 578 | + wdesignkit_invalidate_widget_registry($builder); | |
| 579 | + } | |
| 580 | + | |
| 581 | + $created_files = [ | |
| 582 | + $file_name_full . '.php', | |
| 583 | + $file_name_full . '.json', | |
| 584 | + $file_name_full . '.js', | |
| 585 | + $file_name_full . '.css', | |
| 586 | + ]; | |
| 587 | + if ($thumb_written) { | |
| 588 | + $created_files[] = $file_name_full . '.png'; | |
| 589 | + } | |
| 590 | + | |
| 530 | 591 | return [ |
| 531 | 592 | 'success' => true, |
| 532 | 593 | 'message' => "Widget '{$name}' created successfully for {$builder}.", |
| 533 | 594 | 'widget' => [ |
| @@ -534,14 +595,9 @@ | ||
| 534 | 595 | 'name' => $name, |
| 535 | 596 | 'builder' => $builder, |
| 536 | 597 | 'folder' => $folder_name_full, |
| 537 | 598 | 'widget_id' => $widget_id, // 8-char hash — use with activate/deactivate-widget |
| 538 | - 'files' => [ | |
| 539 | - $file_name_full . '.php', | |
| 540 | - $file_name_full . '.json', | |
| 541 | - $file_name_full . '.js', | |
| 542 | - $file_name_full . '.css', | |
| 543 | - ], | |
| 599 | + 'files' => $created_files, | |
| 544 | 600 | 'version' => $version, |
| 545 | 601 | 'css_class' => $widget_css_class, |
| 546 | 602 | ], |
| 547 | 603 | ]; |
| @@ -948,8 +1004,29 @@ | ||
| 948 | 1004 | return $php_code; |
| 949 | 1005 | } |
| 950 | 1006 | |
| 951 | 1007 | /** |
| 1008 | + * Strip a top-level `namespace ...;` declaration from caller-supplied widget PHP. | |
| 1009 | + * | |
| 1010 | + * Every WDesignKit widget loader (Elementor, Bricks, Gutenberg) instantiates or | |
| 1011 | + * registers the widget class by its bare global name. A namespace declaration makes | |
| 1012 | + * the class resolve as Namespace\Wdkit_xxx, so the loader's `new Wdkit_xxx()` fatals | |
| 1013 | + * with "Class not found" and takes down the whole site. Generated boilerplate never | |
| 1014 | + * declares a namespace — this only guards caller-supplied php_code. | |
| 1015 | + * | |
| 1016 | + * Only the common semicolon form (`namespace Foo\Bar;`) is removed; `use` statements | |
| 1017 | + * are left intact as they remain valid global aliases. | |
| 1018 | + */ | |
| 1019 | +function wdesignkit_mcp_strip_php_namespace(string $php_code): string { | |
| 1020 | + return preg_replace( | |
| 1021 | + '/\bnamespace\s+[A-Za-z0-9_\\\\]+\s*;/', | |
| 1022 | + '', | |
| 1023 | + $php_code, | |
| 1024 | + 1 | |
| 1025 | + ) ?? $php_code; | |
| 1026 | +} | |
| 1027 | + | |
| 1028 | +/** | |
| 952 | 1029 | * Generate Gutenberg / Gutenberg Core block PHP boilerplate. |
| 953 | 1030 | * |
| 954 | 1031 | * Uses wp_upload_dir() for SSL-safe asset URLs. |
| 955 | 1032 | * Uses $file_name (includes unique hash) for function name — no collision possible. |
| @@ -968,8 +1045,48 @@ | ||
| 968 | 1045 | |
| 969 | 1046 | // Collision-safe function name: derived from full $file_name (includes unique hash) |
| 970 | 1047 | $func_name = str_replace(['-', '.'], '_', $file_name); |
| 971 | 1048 | |
| 1049 | + $block_title = 'gutenberg' === $builder ? 'Nexter Blocks (TPGB) Block' : 'Gutenberg Block'; | |
| 1050 | + | |
| 1051 | + // builder=gutenberg targets Nexter Blocks / TPGB. Its editor JS uses the Pmgc | |
| 1052 | + // component framework, which lives in wkit_g_pmgc.js — registered under the handle | |
| 1053 | + // 'wkit-editor-block-pmgc' by the WDesignKit gutenberg loader (editor_assets() in | |
| 1054 | + // class-wdkit-gutenberg-files-load.php) and by nothing else. Declaring that handle | |
| 1055 | + // unconditionally would make WordPress silently drop this entire script — and the | |
| 1056 | + // block with it — on any site where WDesignKit is not active (WP_Dependencies::all_deps() | |
| 1057 | + // discards a script whose dependency was never registered). Whether WDesignKit is | |
| 1058 | + // present is a runtime fact of the destination site, so gate it on the constant. | |
| 1059 | + // Mirrors the widget builder's own output in src/widget-builder/file-creation/gutenberg_file.js. | |
| 1060 | + $deps_block = 'gutenberg' === $builder | |
| 1061 | + ? <<<PHP | |
| 1062 | + | |
| 1063 | + \$wdkit_block_deps = [ 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n' ]; | |
| 1064 | + | |
| 1065 | + if ( defined( 'WDKIT_VERSION' ) ) { | |
| 1066 | + \$wdkit_block_deps[] = 'wkit-editor-block-pmgc'; | |
| 1067 | + } | |
| 1068 | + | |
| 1069 | +PHP | |
| 1070 | + : ''; | |
| 1071 | + | |
| 1072 | + $deps_arg = 'gutenberg' === $builder | |
| 1073 | + ? '$wdkit_block_deps' | |
| 1074 | + : "[ 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n' ]"; | |
| 1075 | + | |
| 1076 | + // The TPGB editor JS stores the per-instance id it styles through Pmgc_CssGenerator. | |
| 1077 | + $attributes_block = 'gutenberg' === $builder | |
| 1078 | + ? <<<PHP | |
| 1079 | + | |
| 1080 | + 'attributes' => [ | |
| 1081 | + 'block_id' => [ | |
| 1082 | + 'type' => 'string', | |
| 1083 | + 'default' => '', | |
| 1084 | + ], | |
| 1085 | + ], | |
| 1086 | +PHP | |
| 1087 | + : ''; | |
| 1088 | + | |
| 972 | 1089 | return <<<PHP |
| 973 | 1090 | <?php |
| 974 | 1091 | /* |
| 975 | 1092 | * Widget Name: {$name} |
| @@ -983,9 +1100,9 @@ | ||
| 983 | 1100 | exit; |
| 984 | 1101 | } |
| 985 | 1102 | |
| 986 | 1103 | /** |
| 987 | - * {$name} - Gutenberg Block | |
| 1104 | + * {$name} - {$block_title} | |
| 988 | 1105 | * {$description} |
| 989 | 1106 | */ |
| 990 | 1107 | if ( ! function_exists( 'wdkit_{$func_name}_register_block' ) ) { |
| 991 | 1108 | function wdkit_{$func_name}_register_block() { |
| @@ -990,13 +1107,13 @@ | ||
| 990 | 1107 | if ( ! function_exists( 'wdkit_{$func_name}_register_block' ) ) { |
| 991 | 1108 | function wdkit_{$func_name}_register_block() { |
| 992 | 1109 | \$upload_dir = wp_upload_dir(); |
| 993 | 1110 | \$baseurl = set_url_scheme( \$upload_dir['baseurl'], is_ssl() ? 'https' : 'http' ); |
| 994 | - | |
| 1111 | +{$deps_block} | |
| 995 | 1112 | wp_register_script( |
| 996 | 1113 | 'wdkit-{$slug}-editor', |
| 997 | 1114 | \$baseurl . '/wdesignkit/{$builder}/{$folder_name}/{$file_name}.js', |
| 998 | - [ 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n' ], | |
| 1115 | + {$deps_arg}, | |
| 999 | 1116 | '{$version}', |
| 1000 | 1117 | true |
| 1001 | 1118 | ); |
| 1002 | 1119 | |
| @@ -1009,9 +1126,9 @@ | ||
| 1009 | 1126 | |
| 1010 | 1127 | register_block_type( 'wdkit/{$slug}', [ |
| 1011 | 1128 | 'editor_script' => 'wdkit-{$slug}-editor', |
| 1012 | 1129 | 'editor_style' => 'wdkit-{$slug}-style', |
| 1013 | - 'style' => 'wdkit-{$slug}-style', | |
| 1130 | + 'style' => 'wdkit-{$slug}-style',{$attributes_block} | |
| 1014 | 1131 | ] ); |
| 1015 | 1132 | } |
| 1016 | 1133 | } |
| 1017 | 1134 | add_action( 'init', 'wdkit_{$func_name}_register_block' ); |
| @@ -1172,11 +1289,127 @@ | ||
| 1172 | 1289 | // WDesignKit executes widget JS in the element_ready scope automatically. |
| 1173 | 1290 | return ''; |
| 1174 | 1291 | } |
| 1175 | 1292 | |
| 1176 | - if ($builder === 'gutenberg' || $builder === 'gutenberg_core') { | |
| 1293 | + if ($builder === 'gutenberg') { | |
| 1294 | + // Nexter Blocks (TPGB) variant. The difference from gutenberg_core is the | |
| 1295 | + // component framework: the inspector is built from Pmgc_PanelTabs/Pmgc_Tab and | |
| 1296 | + // per-instance CSS is emitted through Pmgc_CssGenerator, all served by | |
| 1297 | + // wkit_g_pmgc.js. Every Pmgc component is used behind a guard with a native | |
| 1298 | + // fallback, so the block still registers and renders when Nexter Blocks is not | |
| 1299 | + // installed. Mirrors src/widget-builder/file-creation/gutenberg_file.js. | |
| 1300 | + $pmgc_id = $widget_hash !== '' ? 'wb-' . $widget_hash : 'wb-' . $slug; | |
| 1301 | + | |
| 1177 | 1302 | return <<<JS |
| 1178 | 1303 | /** |
| 1304 | + * {$name} - Nexter Blocks (TPGB) Block | |
| 1305 | + */ | |
| 1306 | +(function(blocks, element, blockEditor, components, i18n) { | |
| 1307 | + var el = element.createElement; | |
| 1308 | + var Fragment = element.Fragment; | |
| 1309 | + var __ = i18n.__; | |
| 1310 | + var InspectorControls = blockEditor.InspectorControls; | |
| 1311 | + var TextControl = components.TextControl; | |
| 1312 | + var TextareaControl = components.TextareaControl; | |
| 1313 | + var PanelBody = components.PanelBody; | |
| 1314 | + | |
| 1315 | + // Registered on window.wp by wkit_g_pmgc.js (script handle 'wkit-editor-block-pmgc'), | |
| 1316 | + // which the WDesignKit gutenberg loader enqueues in the block editor. | |
| 1317 | + var PmgcComponents = window.wp.Pmgc_Components || {}; | |
| 1318 | + var Pmgc_PanelTabs = PmgcComponents.Pmgc_PanelTabs; | |
| 1319 | + var Pmgc_Tab = PmgcComponents.Pmgc_Tab; | |
| 1320 | + var Pmgc_CssGenerator = PmgcComponents.Pmgc_CssGenerator; | |
| 1321 | + | |
| 1322 | + blocks.registerBlockType('wdkit/{$slug}', { | |
| 1323 | + title: __('{$safe_name}', 'wdesignkit'), | |
| 1324 | + description: __('{$safe_desc}', 'wdesignkit'), | |
| 1325 | + icon: el('i', { className: 'wdkit-icon tpae-wdkit-logo' }), | |
| 1326 | + category: '{$safe_cat}', | |
| 1327 | + attributes: { | |
| 1328 | + block_id: { type: 'string', default: '' }, | |
| 1329 | + title: { type: 'string', default: '{$safe_name}' }, | |
| 1330 | + description: { type: 'string', default: '{$safe_desc}' } | |
| 1331 | + }, | |
| 1332 | + | |
| 1333 | + edit: function(props) { | |
| 1334 | + var attributes = props.attributes; | |
| 1335 | + var block_id = props.clientId.substr(0, 6); | |
| 1336 | + | |
| 1337 | + if (attributes.block_id !== block_id) { | |
| 1338 | + props.setAttributes({ block_id: block_id }); | |
| 1339 | + } | |
| 1340 | + | |
| 1341 | + var settings = [ | |
| 1342 | + el(TextControl, { | |
| 1343 | + key: 'title', | |
| 1344 | + label: __('Title', 'wdesignkit'), | |
| 1345 | + value: attributes.title, | |
| 1346 | + onChange: function(val) { props.setAttributes({ title: val }); } | |
| 1347 | + }), | |
| 1348 | + el(TextareaControl, { | |
| 1349 | + key: 'description', | |
| 1350 | + label: __('Description', 'wdesignkit'), | |
| 1351 | + value: attributes.description, | |
| 1352 | + onChange: function(val) { props.setAttributes({ description: val }); } | |
| 1353 | + }) | |
| 1354 | + ]; | |
| 1355 | + | |
| 1356 | + // Nexter Blocks tabbed inspector when TPGB is available, native panel otherwise. | |
| 1357 | + var inspector = (Pmgc_PanelTabs && Pmgc_Tab) | |
| 1358 | + ? el(InspectorControls, null, | |
| 1359 | + el(Pmgc_PanelTabs, null, | |
| 1360 | + el(Pmgc_Tab, { tabTitle: __('Layout', 'wdesignkit') }, settings), | |
| 1361 | + el(Pmgc_Tab, { tabTitle: __('Style', 'wdesignkit') }) | |
| 1362 | + ) | |
| 1363 | + ) | |
| 1364 | + : el(InspectorControls, null, | |
| 1365 | + el(PanelBody, { title: __('{$safe_name} Settings', 'wdesignkit') }, settings) | |
| 1366 | + ); | |
| 1367 | + | |
| 1368 | + if (attributes.block_id && Pmgc_CssGenerator) { | |
| 1369 | + var node = document.getElementsByClassName('tpgb-block-' + block_id); | |
| 1370 | + if (null != node && 'undefined' !== typeof node) { | |
| 1371 | + Pmgc_CssGenerator(attributes, 'wdkit', '{$pmgc_id}', block_id, false, props.clientId); | |
| 1372 | + } | |
| 1373 | + } | |
| 1374 | + | |
| 1375 | + return el(Fragment, null, inspector, | |
| 1376 | + el('div', { className: 'wkit-wb-Widget_{$widget_hash} wdkit-block-' + block_id }, | |
| 1377 | + el('h3', {}, attributes.title), | |
| 1378 | + el('p', {}, attributes.description) | |
| 1379 | + ) | |
| 1380 | + ); | |
| 1381 | + }, | |
| 1382 | + | |
| 1383 | + save: function(props) { | |
| 1384 | + var attributes = props.attributes; | |
| 1385 | + var block_id = attributes.block_id; | |
| 1386 | + var styleCss = Pmgc_CssGenerator | |
| 1387 | + ? Pmgc_CssGenerator(attributes, 'wdkit', '{$pmgc_id}', block_id, true) | |
| 1388 | + : ''; | |
| 1389 | + | |
| 1390 | + return el(Fragment, null, | |
| 1391 | + el('div', { className: 'wkit-wb-Widget_{$widget_hash} wdkit-block-' + block_id }, | |
| 1392 | + el('h3', {}, attributes.title), | |
| 1393 | + el('p', {}, attributes.description) | |
| 1394 | + ), | |
| 1395 | + el('style', null, styleCss) | |
| 1396 | + ); | |
| 1397 | + } | |
| 1398 | + }); | |
| 1399 | +})( | |
| 1400 | + window.wp.blocks, | |
| 1401 | + window.wp.element, | |
| 1402 | + window.wp.blockEditor, | |
| 1403 | + window.wp.components, | |
| 1404 | + window.wp.i18n | |
| 1405 | +); | |
| 1406 | +JS; | |
| 1407 | + } | |
| 1408 | + | |
| 1409 | + if ($builder === 'gutenberg_core') { | |
| 1410 | + return <<<JS | |
| 1411 | +/** | |
| 1179 | 1412 | * {$name} - Gutenberg Block |
| 1180 | 1413 | */ |
| 1181 | 1414 | (function(blocks, element, blockEditor, components, i18n) { |
| 1182 | 1415 | var el = element.createElement; |
| @@ -2238,9 +2471,9 @@ | ||
| 2238 | 2471 | if ($wdk_type === '') { |
| 2239 | 2472 | return null; // Unsupported type — skip |
| 2240 | 2473 | } |
| 2241 | 2474 | |
| 2242 | - $json_name = $wdk_type . '_' . $php_name; | |
| 2475 | + $json_name = $php_name; | |
| 2243 | 2476 | $label = wdesignkit_mcp_php_get_string($args, 'label'); |
| 2244 | 2477 | |
| 2245 | 2478 | $ctrl = [ |
| 2246 | 2479 | '_php_name' => $php_name, // temp — removed after map update |