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 / templates / wdesignkit-update-template.php

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

174 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ability: Update an existing WDesignKit cloud template's data.
4 */
5
6 declare(strict_types=1);
7
8 if (!defined('ABSPATH')) {
9 exit();
10 }
11
12 wp_register_ability('wdesignkit/update-template', [
13 'label' => __('Update WDesignKit Template', 'wdesignkit'),
14 'description' => __(
15 'Updates an existing user-saved WDesignKit cloud template in place. Pass the template id (from list-templates or find-template) together with the new builder data. Optionally include a source post_id to refresh the nxt-* custom meta captured with the template.',
16 'wdesignkit',
17 ),
18 'category' => 'wdesignkit',
19 'input_schema' => [
20 'type' => 'object',
21 'properties' => [
22 'id' => [
23 'type' => 'string',
24 'description' => 'Template ID to update (from wdesignkit/list-templates or wdesignkit/find-template).',
25 ],
26 'data' => [
27 'type' => 'string',
28 'description' => 'New serialized builder data that replaces the current template body.',
29 ],
30 'post_id' => [
31 'type' => 'string',
32 'description' => 'Optional source post ID — used to refresh nxt-* custom meta on the saved template.',
33 ],
34 'type' => [
35 'type' => 'string',
36 'description' => 'Template type (e.g. "page", "section", "block"). Pass-through to the cloud API.',
37 ],
38 'global_data' => [
39 'type' => 'object',
40 'description' => 'Optional global data (colors, typography, etc.) to bundle with the update.',
41 ],
42 ],
43 'required' => ['id', 'data', 'type'],
44 'additionalProperties' => false,
45 ],
46 'output_schema' => [
47 'type' => 'object',
48 'properties' => [
49 'success' => ['type' => 'boolean'],
50 'message' => ['type' => 'string'],
51 'response' => ['type' => ['object', 'array']],
52 ],
53 ],
54 'execute_callback' => 'wdesignkit_mcp_update_template',
55 'permission_callback' => 'wdesignkit_mcp_permission_callback',
56 'meta' => [
57 'public' => true,
58 'show_in_rest' => true,
59 'mcp' => ['public' => true],
60 'annotations' => [
61 'instructions' => implode("\n", [
62 'Updates an existing cloud template by ID. Requires WDesignKit login.',
63 'data REPLACES the saved template body — always derive it from the latest known content.',
64 'To swap one saved template\'s content for a freshly downloaded preset, prefer wdesignkit/replace-template — it adds the confirmation guardrail.',
65 'This updates the template BODY only. The cloud update endpoint has no image field: to change an',
66 'existing template\'s preview thumbnail use wdesignkit/save-template-image with the same template_id.',
67 ]),
68 'readonly' => false,
69 'destructive' => false,
70 'idempotent' => true,
71 ],
72 ],
73 ]);
74
75 function wdesignkit_mcp_update_template(array $input): array {
76 set_time_limit(90);
77
78 $auth = wdesignkit_mcp_template_get_auth();
79 if (empty($auth['logged_in'])) {
80 return ['success' => false, 'message' => $auth['message'] ?? 'Not logged in.'];
81 }
82
83 $template_id = sanitize_text_field((string) ($input['id'] ?? ''));
84 $data = (string) ($input['data'] ?? '');
85 $post_id = sanitize_text_field((string) ($input['post_id'] ?? ''));
86 $type = sanitize_text_field((string) ($input['type'] ?? ''));
87 $global_data = is_array($input['global_data'] ?? null) ? $input['global_data'] : [];
88
89 if ($template_id === '' || $data === '') {
90 return ['success' => false, 'message' => 'id and data are both required.'];
91 }
92
93 if ($type === '') {
94 return ['success' => false, 'message' => 'type is required. Pass one of: page, section, block.'];
95 }
96
97 // Wrap the replacement body in the envelope the cloud, the importer and the editor JS expect —
98 // { file_type, title, page_id, content, el_type, settings } with the layout in `content`, the
99 // same shape the Save Template UI builds.
100 //
101 // Writing the raw layout instead broke both builders:
102 // Elementor — the layout decodes to a LIST, and adding the string key 'custom_meta' to a
103 // list makes wp_json_encode() emit an object, turning the indices into "0","1",…
104 // Gutenberg — block markup is a STRING, so json_decode() returned null, the is_array()
105 // branch never ran, and the markup was stored bare with no envelope. The editor
106 // then runs JSON.parse() on it and throws.
107 //
108 // This ability has no `builder` input, so infer it: Elementor data decodes to an array,
109 // Gutenberg content is serialized block markup.
110 $decoded_content = json_decode($data, true);
111 $is_elementor = is_array($decoded_content);
112
113 $settings = [];
114 if ($post_id !== '') {
115 $page_settings = get_post_meta((int) $post_id, '_elementor_page_settings', true);
116 if (is_array($page_settings) && !empty($page_settings)) {
117 $settings = $page_settings;
118 }
119 }
120
121 $envelope = [
122 'file_type' => $is_elementor ? 'elementor' : 'wp_block',
123 'title' => '',
124 'page_id' => $post_id !== '' ? (int) $post_id : 0,
125 'content' => $is_elementor ? $decoded_content : $data,
126 'el_type' => '',
127 'settings' => $settings,
128 ];
129
130 if ($post_id !== '') {
131 $custom_fields = [];
132 foreach ((array) get_post_custom($post_id) as $key => $value) {
133 if (is_string($key) && str_contains($key, 'nxt-')) {
134 $custom_fields[$key] = $value;
135 }
136 }
137 if (!empty($custom_fields)) {
138 // On the envelope (an object), so it cannot reshape `content`.
139 $envelope['custom_meta'] = $custom_fields;
140 }
141 }
142
143 $data = wp_json_encode($envelope);
144
145 $args = [
146 'data' => $data,
147 'post_id' => $post_id,
148 'token' => $auth['token'],
149 'type' => 'update_template', // server dispatches on this value — template type (page/section) is not used by the update path
150 'id' => $template_id,
151 'global_data' => $global_data,
152 'remove' => 'yes',
153 ];
154
155 $response = wdesignkit_mcp_template_cloud_call('existing_template', $args, 'form');
156
157 // The cloud returns HTTP 200 with an empty body when the template ID does not exist
158 // or the account has no permission to update it. An empty body cannot confirm success —
159 // surface it as a failure so callers are not misled by success:true with no message.
160 if (array_key_exists('raw', $response) && ($response['raw'] === '' || $response['raw'] === null)) {
161 return [
162 'success' => false,
163 'message' => 'Cloud returned no confirmation for update. The template ID may not exist or the account has no permission to update it. Verify with wdesignkit/find-template.',
164 'response' => $response,
165 ];
166 }
167
168 return [
169 'success' => (bool) ($response['success'] ?? false),
170 'message' => $response['message'] ?? $response['massage'] ?? '',
171 'response' => $response,
172 ];
173 }
174