| 1 |
<?php |
| 2 |
/** |
| 3 |
* Ability: Export a local WDesignKit widget as a ZIP archive and return its download URL. |
| 4 |
*/ |
| 5 |
|
| 6 |
declare(strict_types=1); |
| 7 |
|
| 8 |
if (!defined('ABSPATH')) { |
| 9 |
exit(); |
| 10 |
} |
| 11 |
|
| 12 |
wp_register_ability('wdesignkit/export-widget', [ |
| 13 |
'label' => __('Export WDesignKit Widget', 'wdesignkit'), |
| 14 |
'description' => __( |
| 15 |
'Packages a local widget\'s JSON config (and thumbnail image if present) into a ZIP archive inside the same builder directory and returns the download URL. The resulting .zip can be imported on another site via wdesignkit/import-widget.', |
| 16 |
'wdesignkit', |
| 17 |
), |
| 18 |
'category' => 'wdesignkit', |
| 19 |
'input_schema' => [ |
| 20 |
'type' => 'object', |
| 21 |
'properties' => [ |
| 22 |
'builder' => [ |
| 23 |
'type' => 'string', |
| 24 |
'description' => 'Builder type the widget belongs to.', |
| 25 |
'enum' => ['elementor', 'gutenberg', 'gutenberg_core', 'bricks'], |
| 26 |
], |
| 27 |
'folder' => [ |
| 28 |
'type' => 'string', |
| 29 |
'description' => 'Widget folder name (from wdesignkit/list-widgets).', |
| 30 |
], |
| 31 |
], |
| 32 |
'required' => ['builder', 'folder'], |
| 33 |
'additionalProperties' => false, |
| 34 |
], |
| 35 |
'output_schema' => [ |
| 36 |
'type' => 'object', |
| 37 |
'properties' => [ |
| 38 |
'success' => ['type' => 'boolean'], |
| 39 |
'message' => ['type' => 'string'], |
| 40 |
'download_url' => ['type' => 'string'], |
| 41 |
'zip_path' => ['type' => 'string'], |
| 42 |
], |
| 43 |
], |
| 44 |
'execute_callback' => 'wdesignkit_mcp_export_widget', |
| 45 |
'permission_callback' => 'wdesignkit_mcp_permission_callback', |
| 46 |
'meta' => [ |
| 47 |
'public' => true, |
| 48 |
'show_in_rest' => true, |
| 49 |
'mcp' => ['public' => true], |
| 50 |
'annotations' => [ |
| 51 |
'instructions' => implode("\n", [ |
| 52 |
'Creates a .wdk ZIP archive (JSON + thumbnail) for the named widget.', |
| 53 |
'Does NOT require cloud login — this is purely local.', |
| 54 |
'Returns a direct download_url (within the WP uploads directory) the user can click to save the file.', |
| 55 |
'Use wdesignkit/list-widgets to find the correct folder and builder values.', |
| 56 |
]), |
| 57 |
'readonly' => false, |
| 58 |
'destructive' => false, |
| 59 |
'idempotent' => true, |
| 60 |
], |
| 61 |
], |
| 62 |
]); |
| 63 |
|
| 64 |
function wdesignkit_mcp_export_widget(array $input): array { |
| 65 |
// Timeout guard: local ZipArchive creation — purely filesystem, should be fast. |
| 66 |
set_time_limit(30); |
| 67 |
|
| 68 |
if (!defined('WDKIT_BUILDER_PATH') || !defined('WDKIT_SERVER_PATH')) { |
| 69 |
return ['success' => false, 'message' => 'WDesignKit plugin is not active.']; |
| 70 |
} |
| 71 |
|
| 72 |
if (!class_exists('ZipArchive')) { |
| 73 |
return ['success' => false, 'message' => 'PHP ZipArchive extension is not available on this server.']; |
| 74 |
} |
| 75 |
|
| 76 |
$builder = sanitize_text_field((string) ($input['builder'] ?? '')); |
| 77 |
$folder = sanitize_file_name((string) ($input['folder'] ?? '')); |
| 78 |
|
| 79 |
$allowed_builders = ['elementor', 'gutenberg', 'gutenberg_core', 'bricks']; |
| 80 |
if (!in_array($builder, $allowed_builders, true)) { |
| 81 |
return ['success' => false, 'message' => 'Invalid builder type.']; |
| 82 |
} |
| 83 |
|
| 84 |
$widget_dir = WDKIT_BUILDER_PATH . '/' . $builder . '/' . $folder; |
| 85 |
|
| 86 |
if (!is_dir($widget_dir)) { |
| 87 |
return ['success' => false, 'message' => "Widget folder not found: {$builder}/{$folder}"]; |
| 88 |
} |
| 89 |
|
| 90 |
$real_widget = realpath($widget_dir); |
| 91 |
$real_base = realpath(WDKIT_BUILDER_PATH); |
| 92 |
if (!$real_widget || !$real_base || strpos($real_widget, $real_base . DIRECTORY_SEPARATOR) !== 0) { |
| 93 |
return ['success' => false, 'message' => 'Invalid widget path.']; |
| 94 |
} |
| 95 |
|
| 96 |
$files = array_diff(@scandir($widget_dir) ?: [], ['.', '..']); |
| 97 |
|
| 98 |
$json_file = null; |
| 99 |
$img_file = null; |
| 100 |
$img_ext = null; |
| 101 |
|
| 102 |
foreach ($files as $f) { |
| 103 |
$ext = pathinfo($f, PATHINFO_EXTENSION); |
| 104 |
if ($ext === 'json') { |
| 105 |
$json_file = $widget_dir . '/' . $f; |
| 106 |
} elseif (in_array($ext, ['jpg', 'jpeg', 'png', 'webp'], true) && $img_file === null) { |
| 107 |
$img_file = $widget_dir . '/' . $f; |
| 108 |
$img_ext = $ext; |
| 109 |
} |
| 110 |
} |
| 111 |
|
| 112 |
if (!$json_file || !file_exists($json_file)) { |
| 113 |
return ['success' => false, 'message' => "No JSON config found in widget folder {$builder}/{$folder}"]; |
| 114 |
} |
| 115 |
|
| 116 |
$archive_name = str_replace('-', '_', $folder) . '.zip'; |
| 117 |
$archive_path = WDKIT_BUILDER_PATH . '/' . $builder . '/' . $archive_name; |
| 118 |
|
| 119 |
$zip = new ZipArchive(); |
| 120 |
if ($zip->open($archive_path, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { |
| 121 |
return ['success' => false, 'message' => 'Could not create ZIP archive. Check filesystem permissions.']; |
| 122 |
} |
| 123 |
|
| 124 |
$zip->addFile($json_file, str_replace('-', '_', $folder) . '.json'); |
| 125 |
|
| 126 |
if ($img_file && file_exists($img_file)) { |
| 127 |
$zip->addFile($img_file, str_replace('-', '_', $folder) . '.' . $img_ext); |
| 128 |
} |
| 129 |
|
| 130 |
$zip->close(); |
| 131 |
|
| 132 |
$download_url = WDKIT_SERVER_PATH . '/' . $builder . '/' . $archive_name; |
| 133 |
|
| 134 |
return [ |
| 135 |
'success' => true, |
| 136 |
'message' => "Widget '{$folder}' packaged as {$archive_name}.", |
| 137 |
'download_url' => $download_url, |
| 138 |
'zip_path' => $archive_path, |
| 139 |
]; |
| 140 |
} |
| 141 |
|