PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.7.0
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.7.0
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-push-widget.php +52 -24 2.5.1 → 2.7.0 View file →
@@ -9,12 +9,12 @@
9 9 exit();
10 10 }
11 11
12 12 wp_register_ability('wdesignkit/push-widget', [
13 - 'label' => __('Push WDesignKit Widget to Cloud', 'sprout-mcp'),
13 + 'label' => __('Push WDesignKit Widget to Cloud', 'wdesignkit'),
14 14 'description' => __(
15 15 'Uploads a local widget to the WDesignKit cloud marketplace. Reads the widget files from disk, packages them, and posts to the save_widget endpoint. Requires cloud login. After a successful push the cloud returns a marketplace record ID (r_id) which is written back to the local JSON so future version checks work correctly.',
16 - 'sprout-mcp',
16 + 'wdesignkit',
17 17 ),
18 18 'category' => 'wdesignkit',
19 19 'input_schema' => [
20 20 'type' => 'object',
@@ -41,14 +41,16 @@
41 41 'type' => 'object',
42 42 'properties' => [
43 43 'success' => ['type' => 'boolean'],
44 44 'message' => ['type' => 'string'],
45 + 'r_id' => ['type' => 'integer', 'description' => 'Cloud marketplace record ID on success (0 if the cloud did not return one). Use this — not a value dug out of "response" — with wdesignkit/download-widget or wdesignkit/manage-workspace-widget to reference this widget afterwards.'],
45 46 'response' => ['type' => 'object'],
46 47 ],
47 48 ],
48 49 'execute_callback' => 'wdesignkit_mcp_push_widget',
49 - 'permission_callback' => 'sprout_mcp_permission_callback',
50 + 'permission_callback' => 'wdesignkit_mcp_permission_callback',
50 51 'meta' => [
52 + 'public' => true,
51 53 'show_in_rest' => true,
52 54 'mcp' => ['public' => true],
53 55 'annotations' => [
54 56 'instructions' => implode("\n", [
@@ -54,8 +56,10 @@
54 56 'instructions' => implode("\n", [
55 57 'Pushes a local widget to the WDesignKit cloud marketplace. Requires login.',
56 58 'type defaults to "new". If the widget already has an r_id in its JSON, use "update".',
57 59 'After a successful push the local JSON is updated with the returned r_id and cloud image URL.',
60 + 'The response\'s top-level "r_id" field is the value to use afterwards — with wdesignkit/download-widget,',
61 + 'wdesignkit/manage-workspace-widget, or wdesignkit/get-my-cloud-widgets to find it again later.',
58 62 ]),
59 63 'readonly' => false,
60 64 'destructive' => false,
61 65 'idempotent' => false,
@@ -158,23 +162,25 @@
158 162 'hint' => 'The cloud rejects re-pushes with the same version number. Increment widget_version in the JSON config first.',
159 163 ];
160 164 }
161 165
162 - // Pre-flight: thumbnail is required by the cloud save_widget endpoint.
163 - // If no image file is present the cloud silently returns HTTP 200 with an
164 - // empty body and the push fails without explanation. Fail early here so
165 - // the caller gets a clear, actionable message rather than the generic
166 - // "empty array" response.
167 - if (!$img_path || !file_exists($img_path)) {
168 - return [
169 - 'success' => false,
170 - 'message' => "Widget '{$title}' has no thumbnail image. Add a thumbnail file (jpg, jpeg, png, or webp) to the widget folder before pushing to the marketplace.",
171 - ];
166 + // Thumbnail is OPTIONAL, matching the manual "Push to Cloud" UI. SetSaveWidget
167 + // (wdk-server WdkitPluginController) reads w_image with a '' default and only stores
168 + // a file inside if( !empty($w_image) ) — it never validates or rejects an imageless
169 + // push. Blocking here made this ability stricter than the product it wraps. (bug: 86d3rc7q4)
170 + $w_image_body = '';
171 + $w_imgext = '';
172 +
173 + if ($img_path && file_exists($img_path)) {
174 + $w_image_body = @file_get_contents($img_path);
175 + $w_imgext = $img_ext;
176 +
177 + if (false === $w_image_body) {
178 + $w_image_body = '';
179 + $w_imgext = '';
180 + }
172 181 }
173 182
174 - $w_image_body = @file_get_contents($img_path);
175 - $w_imgext = $img_ext;
176 -
177 183 $array_data = [
178 184 'token' => $auth['token'],
179 185 // The cloud save_widget endpoint only understands 'add' — it determines
180 186 // new vs update by checking whether the widget already exists in the DB
@@ -215,15 +221,15 @@
215 221 $body = wp_remote_retrieve_body($response);
216 222 $data = json_decode($body, true);
217 223
218 224 // HTTP 200 with an empty/null body ( [], '', null ) means the cloud silently rejected
219 - // the request — common when the widget is missing a thumbnail image, the account
220 - // lacks marketplace publish rights, or a required metadata field is absent.
221 - // Guard covers both json_decode('[]') → [] and json_decode('') → null.
225 + // the request — the account lacks marketplace publish rights, or a required metadata
226 + // field is absent. (A missing thumbnail is NOT a cause: save_widget stores the image
227 + // only when one is sent.) Guard covers both json_decode('[]') → [] and json_decode('') → null.
222 228 if (200 === (int) $status && ($data === null || (is_array($data) && empty($data)))) {
223 229 return [
224 230 'success' => false,
225 - 'message' => 'Invalid API response format: empty array. The cloud returned HTTP 200 but an empty response body. Check that the widget has a thumbnail image, that the WDesignKit account has marketplace publish rights, and that all required metadata fields (name, widget_id, widget_version) are present.',
231 + 'message' => 'Invalid API response format: empty array. The cloud returned HTTP 200 but an empty response body. Check that the WDesignKit account has marketplace publish rights and that all required metadata fields (name, widget_id, widget_version) are present.',
226 232 'response' => ['raw' => $body],
227 233 ];
228 234 }
229 235
@@ -244,18 +250,36 @@
244 250 'response' => wdesignkit_mcp_ensure_object($data, $body),
245 251 ];
246 252 }
247 253
248 - // Write back r_id and updated image URL to local JSON
254 + // Write back r_id and updated image URL to local JSON.
255 + // $res_data IS the cloud's "data" object already ({imgurl, id}) — reading a further
256 + // ['data'] level off of it (the previous code) always missed, so img_url and the id were
257 + // silently '' / 0 on every push regardless of what the cloud sent. 'id' is kit_widgets.id
258 + // (added server-side in SetSaveWidget) — the identifier download-widget / get-my-cloud-widgets
259 + // / manage-workspace-widget all key on; it is NOT the same thing as the widgetdata.r_id this
260 + // ability's own re-publish check reads (that column tracks copy lineage, not "my widget's id"),
261 + // but this ability's response has always called it r_id, so keep that external name.
249 262 $res_data = is_array($data['data'] ?? null) ? $data['data'] : [];
250 - $img_url = $res_data['data']['imgurl'] ?? '';
251 - $new_r_id = $res_data['data']['r_id'] ?? ($res_data['r_id'] ?? 0);
263 + $img_url = $res_data['imgurl'] ?? '';
264 + $new_r_id = $res_data['id'] ?? ($res_data['r_id'] ?? 0);
252 265
266 + $json_dirty = false;
267 +
253 268 if ($new_r_id) {
254 269 $json_data['widget_data']['widgetdata']['r_id'] = $new_r_id;
270 + $json_dirty = true;
255 271 }
256 - if ($img_url && $json_path) {
272 + if ($img_url) {
257 273 $json_data['widget_data']['widgetdata']['w_image'] = $img_url;
274 + $json_dirty = true;
275 + }
276 +
277 + // The write must run whenever EITHER field changed — previously it only ran inside the
278 + // $img_url check, so an imageless push (now valid: the thumbnail pre-flight was removed
279 + // in 86d3rc7q4) got a real r_id back from the cloud but never persisted it, leaving the
280 + // widget permanently unrecognized as already-published on every later push/download call.
281 + if ($json_dirty && $json_path) {
258 282 @file_put_contents($json_path, wp_json_encode($json_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
259 283 }
260 284
261 285 return [
@@ -260,7 +284,11 @@
260 284
261 285 return [
262 286 'success' => true,
263 287 'message' => "Widget '{$title}' pushed to cloud." . ($new_r_id ? " Marketplace record ID: {$new_r_id}." : ''),
288 + // Surfaced as a first-class field — callers previously had to dig it out of the raw
289 + // cloud response shape (response.data.data.r_id), which QA correctly flagged as
290 + // effectively undiscoverable.
291 + 'r_id' => (int) $new_r_id,
264 292 'response' => wdesignkit_mcp_ensure_object($data, $body),
265 293 ];
266 294 }