PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.7.0
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.7.0
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 1.0.16 All 129 releases
wdesignkit / includes / abilities / workspace / wdesignkit-workspace.php

wdesignkit-workspace.php in WDesignKit – AI Templates, Widget Builder & MCP Workflow 2.7.0, at includes/abilities/workspace/wdesignkit-workspace.php

541 lines 20.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Abilities: Create, update, delete, and retrieve WDesignKit cloud workspaces, plus Shared With Me.
4 */
5
6 declare(strict_types=1);
7
8 if (!defined('ABSPATH')) {
9 exit();
10 }
11
12 wp_register_ability('wdesignkit/create-workspace', [
13 'label' => __('Create WDesignKit Workspace', 'wdesignkit'),
14 'description' => __(
15 'Creates a new cloud workspace in WDesignKit. A workspace is a shared environment for organising templates, widgets, and code snippets for team collaboration. Requires cloud login.',
16 'wdesignkit',
17 ),
18 'category' => 'wdesignkit',
19 'input_schema' => [
20 'type' => 'object',
21 'properties' => [
22 'title' => [
23 'type' => 'string',
24 'description' => 'Name/title for the new workspace.',
25 ],
26 'builder' => [
27 'type' => 'string',
28 'description' => 'Primary page builder for the workspace (e.g. "elementor", "gutenberg", "bricks").',
29 ],
30 ],
31 'required' => ['title'],
32 'additionalProperties' => false,
33 ],
34 'output_schema' => [
35 'type' => 'object',
36 'properties' => [
37 'success' => ['type' => 'boolean'],
38 'message' => ['type' => 'string'],
39 'wid' => ['type' => ['integer', 'null']],
40 'response' => ['type' => ['object', 'array']],
41 ],
42 ],
43 'execute_callback' => 'wdesignkit_mcp_create_workspace',
44 'permission_callback' => 'wdesignkit_mcp_permission_callback',
45 'meta' => [
46 'public' => true,
47 'show_in_rest' => true,
48 'mcp' => ['public' => true],
49 'annotations' => [
50 'instructions' => implode("\n", [
51 'Creates a new cloud workspace. Requires cloud login.',
52 'The created workspace ID is returned in the wid field — use it for all subsequent workspace operations.',
53 'Use wdesignkit/get-workspace-data to inspect the workspace after creation.',
54 'A workspace groups templates, widgets, and code snippets for team collaboration.',
55 ]),
56 'readonly' => false,
57 'destructive' => false,
58 'idempotent' => false,
59 ],
60 ],
61 ]);
62
63 wp_register_ability('wdesignkit/delete-workspace', [
64 'label' => __('Delete WDesignKit Workspace', 'wdesignkit'),
65 'description' => __(
66 'Permanently deletes a WDesignKit cloud workspace by its workspace ID. Only the workspace container is removed — cloud templates, widgets, and snippets inside it are not deleted. Requires cloud login and confirm: true.',
67 'wdesignkit',
68 ),
69 'category' => 'wdesignkit',
70 'input_schema' => [
71 'type' => 'object',
72 'properties' => [
73 'wid' => [
74 'type' => 'integer',
75 'description' => 'Workspace ID to delete (from the user workspace list or wdesignkit/create-workspace response).',
76 ],
77 'confirm' => [
78 'type' => 'boolean',
79 'description' => 'Must be true to execute the deletion. Omit or false for a dry-run preview.',
80 ],
81 ],
82 'required' => ['wid'],
83 'additionalProperties' => false,
84 ],
85 'output_schema' => [
86 'type' => 'object',
87 'properties' => [
88 'success' => ['type' => 'boolean'],
89 'message' => ['type' => 'string'],
90 'response' => ['type' => ['object', 'array']],
91 'dry_run' => ['type' => 'boolean'],
92 ],
93 ],
94 'execute_callback' => 'wdesignkit_mcp_delete_workspace',
95 'permission_callback' => 'wdesignkit_mcp_permission_callback',
96 'meta' => [
97 'public' => true,
98 'show_in_rest' => true,
99 'mcp' => ['public' => true],
100 'annotations' => [
101 'instructions' => implode("\n", [
102 'Deletes a workspace from the WDesignKit cloud. Requires cloud login.',
103 'confirm: true is required to execute. Without it the call returns a dry-run preview.',
104 'Only the workspace container is removed — items inside are not deleted from the cloud.',
105 ]),
106 'readonly' => false,
107 'destructive' => true,
108 'idempotent' => false,
109 ],
110 ],
111 ]);
112
113 wp_register_ability('wdesignkit/update-workspace', [
114 'label' => __('Update WDesignKit Workspace', 'wdesignkit'),
115 'description' => __(
116 'Updates the title and/or primary builder of an existing WDesignKit cloud workspace. Covers both "Rename Workspace" and "Update Workspace Details". Requires cloud login.',
117 'wdesignkit',
118 ),
119 'category' => 'wdesignkit',
120 'input_schema' => [
121 'type' => 'object',
122 'properties' => [
123 'wid' => [
124 'type' => 'integer',
125 'description' => 'Workspace ID to update.',
126 ],
127 'title' => [
128 'type' => 'string',
129 'description' => 'New title/name for the workspace.',
130 ],
131 'builder' => [
132 'type' => 'string',
133 'description' => 'Primary page builder (e.g. "elementor", "gutenberg", "bricks").',
134 ],
135 ],
136 'required' => ['wid'],
137 'additionalProperties' => false,
138 ],
139 'output_schema' => [
140 'type' => 'object',
141 'properties' => [
142 'success' => ['type' => 'boolean'],
143 'message' => ['type' => 'string'],
144 'response' => ['type' => ['object', 'array']],
145 ],
146 ],
147 'execute_callback' => 'wdesignkit_mcp_update_workspace',
148 'permission_callback' => 'wdesignkit_mcp_permission_callback',
149 'meta' => [
150 'public' => true,
151 'show_in_rest' => true,
152 'mcp' => ['public' => true],
153 'annotations' => [
154 'instructions' => implode("\n", [
155 'Updates workspace title and/or builder. Requires cloud login.',
156 'Provide at least one of title or builder to update.',
157 'This covers both "Rename Workspace" and "Update Workspace Details" operations.',
158 ]),
159 'readonly' => false,
160 'destructive' => false,
161 'idempotent' => true,
162 ],
163 ],
164 ]);
165
166 wp_register_ability('wdesignkit/get-workspace-data', [
167 'label' => __('Get WDesignKit Workspace Data', 'wdesignkit'),
168 'description' => __(
169 'Retrieves full data for a specific WDesignKit cloud workspace including its templates, widgets, code snippets, member list, roles, and totals. Uses the v2 API. Requires cloud login.',
170 'wdesignkit',
171 ),
172 'category' => 'wdesignkit',
173 'input_schema' => [
174 'type' => 'object',
175 'properties' => [
176 'wid' => [
177 'type' => 'integer',
178 'description' => 'Workspace ID to fetch data for.',
179 ],
180 ],
181 'required' => ['wid'],
182 'additionalProperties' => false,
183 ],
184 'output_schema' => [
185 'type' => 'object',
186 'properties' => [
187 'success' => ['type' => 'boolean'],
188 'message' => ['type' => 'string'],
189 'response' => ['type' => ['object', 'array']],
190 ],
191 ],
192 'execute_callback' => 'wdesignkit_mcp_get_workspace_data',
193 'permission_callback' => 'wdesignkit_mcp_permission_callback',
194 'meta' => [
195 'public' => true,
196 'show_in_rest' => true,
197 'mcp' => ['public' => true],
198 'annotations' => [
199 'instructions' => implode("\n", [
200 'Fetches full workspace data including templates, widgets, and snippets. Requires cloud login.',
201 'Response includes work_templates, work_widgets, work_snippets, share_user, and workspace metadata.',
202 'wid comes from the workspace list returned by wdesignkit/get-login-status or wdesignkit/create-workspace.',
203 'Works for all account tiers (free and Pro). Uses get_user_info internally — no v2 Pro-gated endpoint.',
204 ]),
205 'readonly' => true,
206 'destructive' => false,
207 'idempotent' => true,
208 ],
209 ],
210 ]);
211
212 wp_register_ability('wdesignkit/get-shared-with-me', [
213 'label' => __('Get WDesignKit Shared With Me', 'wdesignkit'),
214 'description' => __(
215 'Lists templates and/or widgets that have been shared with the current user through WDesignKit cloud workspace collaboration. Supports type and builder filtering with pagination. Requires cloud login.',
216 'wdesignkit',
217 ),
218 'category' => 'wdesignkit',
219 'input_schema' => [
220 'type' => 'object',
221 'properties' => [
222 'type' => [
223 'type' => 'string',
224 'description' => 'Content type filter: "template" or "widget". Leave empty for all shared items.',
225 ],
226 'builder' => [
227 'type' => 'string',
228 'description' => 'Filter by page builder (e.g. "elementor", "gutenberg", "bricks"). Leave empty for all builders.',
229 ],
230 'page' => [
231 'type' => 'integer',
232 'description' => 'Page number (1-based). Defaults to 1.',
233 'minimum' => 1,
234 ],
235 'per_page' => [
236 'type' => 'integer',
237 'description' => 'Items per page. Defaults to 12.',
238 'minimum' => 1,
239 'maximum' => 100,
240 ],
241 ],
242 'additionalProperties' => false,
243 ],
244 'output_schema' => [
245 'type' => 'object',
246 'properties' => [
247 'success' => ['type' => 'boolean'],
248 'message' => ['type' => 'string'],
249 'response' => ['type' => ['object', 'array']],
250 ],
251 ],
252 'execute_callback' => 'wdesignkit_mcp_get_shared_with_me',
253 'permission_callback' => 'wdesignkit_mcp_permission_callback',
254 'meta' => [
255 'public' => true,
256 'show_in_rest' => true,
257 'mcp' => ['public' => true],
258 'annotations' => [
259 'instructions' => implode("\n", [
260 'Lists items shared with the current user via workspace collaboration. Requires cloud login.',
261 'Filter by type ("template"/"widget") and builder.',
262 'Use wdesignkit/get-workspace-data to see all items inside a specific workspace.',
263 ]),
264 'readonly' => true,
265 'destructive' => false,
266 'idempotent' => true,
267 ],
268 ],
269 ]);
270
271 function wdesignkit_mcp_create_workspace(array $input): array {
272 // Timeout guard: cloud create call uses the default timeout.
273 set_time_limit(90);
274
275 if (!defined('WDKIT_SERVER_API_URL')) {
276 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
277 }
278
279 $auth = wdesignkit_mcp_template_get_auth();
280 if (empty($auth['logged_in'])) {
281 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in to WDesignKit cloud.'];
282 }
283
284 $title = sanitize_text_field((string) ($input['title'] ?? ''));
285 $builder = sanitize_text_field((string) ($input['builder'] ?? ''));
286
287 if ($title === '') {
288 return ['success' => false, 'message' => 'title is required.'];
289 }
290
291 // Use 'form' mode (direct wp_remote_post) instead of 'json' (WDesignKit_Data_Query) so we
292 // receive the raw cloud response without any intermediate transformation that might strip keys.
293 $cloud = wdesignkit_mcp_template_cloud_call('manage_workspace', [
294 'token' => $auth['token'],
295 'title' => $title,
296 'builder' => $builder,
297 'wstype' => 'add',
298 ], 'form', 30);
299
300 // Extract the newly created workspace ID from the cloud response.
301 // The cloud returns the new wid as a top-level string/int — check scalar fields first,
302 // then nested arrays. IMPORTANT: guard array access with is_array() so that when 'data'
303 // is a scalar (e.g. "1495"), PHP does not silently coerce it to a string character offset
304 // and return the wrong digit (e.g. "1" → wid=1 instead of 1495).
305 $cloud_data = is_array($cloud['data'] ?? null) ? $cloud['data'] : [];
306 $cloud_ws = is_array($cloud['workspace'] ?? null) ? $cloud['workspace'] : [];
307
308 $wid_raw = $cloud['wid']
309 ?? $cloud['workspace_id']
310 ?? $cloud['id']
311 ?? $cloud_ws['id']
312 ?? $cloud_ws['wid']
313 ?? $cloud_data['id']
314 ?? $cloud_data['wid']
315 ?? $cloud_data['workspace_id']
316 // Last resort: if 'data' is itself a numeric string/int (cloud returns {data:"1495"})
317 ?? (is_numeric($cloud['data'] ?? null) ? $cloud['data'] : null)
318 ?? null;
319 $wid = ($wid_raw !== null && (int) $wid_raw > 0) ? (int) $wid_raw : null;
320
321 // Override the cloud message: the cloud reuses "Workspace Updated" for the create endpoint.
322 // Always return "Workspace Created." on success so callers are not misled.
323 $success = !empty($cloud['success']);
324 $message = $success
325 ? ('Workspace Created.' . ($wid === null ? ' Note: the workspace ID (wid) was not returned by the cloud API — use wdesignkit/get-workspace-data with a known wid or check your workspace list in the WDesignKit dashboard.' : ''))
326 : ($cloud['message'] ?? $cloud['massage'] ?? 'Failed to create workspace.');
327
328 return [
329 'success' => $success,
330 'message' => $message,
331 'wid' => $wid,
332 'response' => $cloud,
333 ];
334 }
335
336 function wdesignkit_mcp_delete_workspace(array $input): array {
337 // Timeout guard: cloud delete call uses the default timeout.
338 set_time_limit(90);
339
340 if (!defined('WDKIT_SERVER_API_URL')) {
341 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
342 }
343
344 $auth = wdesignkit_mcp_template_get_auth();
345 if (empty($auth['logged_in'])) {
346 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in to WDesignKit cloud.'];
347 }
348
349 $wid = (int) ($input['wid'] ?? 0);
350 $confirm = (bool) ($input['confirm'] ?? false);
351
352 if ($wid <= 0) {
353 return ['success' => false, 'message' => 'wid is required.'];
354 }
355
356 if (!$confirm) {
357 return [
358 'success' => false,
359 'message' => "Dry run: would delete workspace ID {$wid}. Pass confirm: true to execute.",
360 'dry_run' => true,
361 ];
362 }
363
364 // Use 'form' mode (wp_remote_post with explicit timeout) to avoid the indefinite
365 // hang caused by WDesignKit_Data_Query having no server-side HTTP timeout guard.
366 $cloud = wdesignkit_mcp_template_cloud_call('manage_workspace', [
367 'token' => $auth['token'],
368 'wid' => $wid,
369 'wstype' => 'remove',
370 ], 'form', 15);
371
372 return [
373 'success' => !empty($cloud['success']),
374 'message' => $cloud['message'] ?? $cloud['massage'] ?? ($cloud['success'] ? 'Workspace deleted.' : 'Failed to delete workspace.'),
375 'response' => $cloud,
376 'dry_run' => false,
377 ];
378 }
379
380 function wdesignkit_mcp_update_workspace(array $input): array {
381 // Timeout guard: cloud update call uses the default timeout.
382 set_time_limit(90);
383
384 if (!defined('WDKIT_SERVER_API_URL')) {
385 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
386 }
387
388 $auth = wdesignkit_mcp_template_get_auth();
389 if (empty($auth['logged_in'])) {
390 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in to WDesignKit cloud.'];
391 }
392
393 $wid = (int) ($input['wid'] ?? 0);
394 $title = isset($input['title']) ? sanitize_text_field((string) $input['title']) : null;
395 $builder = isset($input['builder']) ? sanitize_text_field((string) $input['builder']) : null;
396
397 if ($wid <= 0) {
398 return ['success' => false, 'message' => 'wid is required.'];
399 }
400
401 if ($title === null && $builder === null) {
402 return ['success' => false, 'message' => 'Provide at least one of title or builder to update.'];
403 }
404
405 $args = [
406 'token' => $auth['token'],
407 'wid' => $wid,
408 'wstype' => 'edit',
409 ];
410
411 if ($title !== null) {
412 $args['title'] = $title;
413 }
414
415 if ($builder !== null) {
416 $args['builder'] = $builder;
417 }
418
419 // Use 'form' mode (wp_remote_post directly) instead of 'json' (WDesignKit_Data_Query::get_data).
420 // WDesignKit_Data_Query checks the locally cached user_type before calling the cloud; if the
421 // local licence cache still shows "free" after Pro activation it returns "Permission Denied"
422 // before the request ever leaves the server. Form mode bypasses that local check and lets
423 // the cloud validate permissions using the token.
424 // Use 'form' mode with explicit 15s timeout so the call fails fast when the cloud is
425 // unreachable or when the session token is invalid, instead of hanging for 4+ minutes.
426 $cloud = wdesignkit_mcp_template_cloud_call('manage_workspace', $args, 'form', 15);
427
428 return [
429 'success' => !empty($cloud['success']),
430 'message' => $cloud['message'] ?? $cloud['massage'] ?? ($cloud['success'] ? 'Workspace updated.' : 'Failed to update workspace.'),
431 'response' => $cloud,
432 ];
433 }
434
435 function wdesignkit_mcp_get_workspace_data(array $input): array {
436 // Timeout guard.
437 set_time_limit(90);
438
439 if (!defined('WDKIT_SERVER_API_URL')) {
440 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
441 }
442
443 $auth = wdesignkit_mcp_template_get_auth();
444 if (empty($auth['logged_in'])) {
445 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in to WDesignKit cloud.'];
446 }
447
448 $wid = (int) ($input['wid'] ?? 0);
449
450 if ($wid <= 0) {
451 return ['success' => false, 'message' => 'wid is required.'];
452 }
453
454 // The v2 endpoint (api/v2/wp/workspace/{wid}/get) gates on a non-subscriber user_role and
455 // returns "Permission Denied" for free/subscriber accounts even when they own the workspace.
456 // get_user_info already returns full workspace data (templates, widgets, snippets, share_user)
457 // via WorkspaceList() without any subscription restriction. We extract the matching workspace
458 // by w_id from that response instead — this works for all account tiers.
459 $user_data = wdesignkit_mcp_template_cloud_call('get_user_info', [
460 'token' => $auth['token'],
461 'builder' => '',
462 'site_url' => home_url(),
463 ], 'json');
464
465 if (empty($user_data['success'])) {
466 return [
467 'success' => false,
468 'message' => $user_data['message'] ?? 'Failed to fetch user data from cloud.',
469 'response' => wdesignkit_mcp_ensure_object($user_data, ''),
470 ];
471 }
472
473 $workspaces = $user_data['workspace'] ?? [];
474 if (!is_array($workspaces)) {
475 $workspaces = [];
476 }
477
478 // Find the workspace matching the requested wid.
479 $workspace = null;
480 foreach ($workspaces as $ws) {
481 if (is_array($ws) && isset($ws['w_id']) && (int) $ws['w_id'] === $wid) {
482 $workspace = $ws;
483 break;
484 }
485 }
486
487 if ($workspace === null) {
488 return [
489 'success' => false,
490 'message' => "Workspace {$wid} not found. Verify the wid is correct and belongs to (or is shared with) the authenticated user.",
491 ];
492 }
493
494 return [
495 'success' => true,
496 'message' => '',
497 'response' => $workspace,
498 ];
499 }
500
501 function wdesignkit_mcp_get_shared_with_me(array $input): array {
502 // Timeout guard: cloud shared-with-me call uses the default timeout.
503 set_time_limit(90);
504
505 if (!defined('WDKIT_SERVER_API_URL')) {
506 return ['success' => false, 'message' => 'WDesignKit plugin is not active.'];
507 }
508
509 $auth = wdesignkit_mcp_template_get_auth();
510 if (empty($auth['logged_in'])) {
511 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in to WDesignKit cloud.'];
512 }
513
514 $type = sanitize_text_field((string) ($input['type'] ?? ''));
515 $builder = sanitize_text_field((string) ($input['builder'] ?? ''));
516 $page = max(1, (int) ($input['page'] ?? 1));
517 $per_page = min(100, max(1, (int) ($input['per_page'] ?? 12)));
518
519 $cloud = wdesignkit_mcp_template_cloud_call('shared_with_me', [
520 'token' => $auth['token'],
521 'type' => $type,
522 'builder' => $builder,
523 'CurrentPage' => $page,
524 'ParPage' => $per_page,
525 ], 'form');
526
527 // Normalize empty response: cloud returns an empty body (which the helper converts to
528 // raw:"") when the type filter returns no shared items (e.g. type="widget" with no
529 // shared widgets). Normalise into a consistent content:[]/total:0 structure so callers
530 // always see the same shape regardless of the type parameter.
531 if (array_key_exists('raw', $cloud) && ($cloud['raw'] === '' || $cloud['raw'] === null)) {
532 $cloud = ['success' => true, 'content' => [], 'total' => 0];
533 }
534
535 return [
536 'success' => !empty($cloud['success']),
537 'message' => $cloud['message'] ?? $cloud['massage'] ?? '',
538 'response' => $cloud,
539 ];
540 }
541