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
← All changes | includes/abilities/templates/wdesignkit-replace-template.php +38 -5 2.6.1 → 2.7.0 View file →
@@ -58,8 +58,9 @@
58 58 ],
59 59 'execute_callback' => 'wdesignkit_mcp_replace_template',
60 60 'permission_callback' => 'wdesignkit_mcp_permission_callback',
61 61 'meta' => [
62 + 'public' => true,
62 63 'show_in_rest' => true,
63 64 'mcp' => ['public' => true],
64 65 'annotations' => [
65 66 'instructions' => implode("\n", [
@@ -103,9 +104,42 @@
103 104 'message' => 'Replacement requires explicit confirmation. Re-send with confirm: true after previewing with dry_run: true.',
104 105 ];
105 106 }
106 107
108 + // Wrap the replacement body in the envelope the cloud, the importer and the editor JS expect —
109 + // { file_type, title, page_id, content, el_type, settings } with the layout in `content`, the
110 + // same shape the Save Template UI builds.
111 + //
112 + // Writing the raw layout instead broke both builders:
113 + // Elementor — the layout decodes to a LIST, and adding the string key 'custom_meta' to a
114 + // list makes wp_json_encode() emit an object, turning the indices into "0","1",…
115 + // Gutenberg — block markup is a STRING, so json_decode() returned null, the is_array()
116 + // branch never ran, and the markup was stored bare with no envelope. The editor
117 + // then runs JSON.parse() on it and throws.
118 + //
119 + // This ability has no `builder` input, so infer it: Elementor data decodes to an array,
120 + // Gutenberg content is serialized block markup.
121 + $decoded_content = json_decode($data, true);
122 + $is_elementor = is_array($decoded_content);
123 +
124 + $settings = [];
107 125 if ($post_id !== '') {
126 + $page_settings = get_post_meta((int) $post_id, '_elementor_page_settings', true);
127 + if (is_array($page_settings) && !empty($page_settings)) {
128 + $settings = $page_settings;
129 + }
130 + }
131 +
132 + $envelope = [
133 + 'file_type' => $is_elementor ? 'elementor' : 'wp_block',
134 + 'title' => '',
135 + 'page_id' => $post_id !== '' ? (int) $post_id : 0,
136 + 'content' => $is_elementor ? $decoded_content : $data,
137 + 'el_type' => '',
138 + 'settings' => $settings,
139 + ];
140 +
141 + if ($post_id !== '') {
108 142 $custom_fields = [];
109 143 foreach ((array) get_post_custom($post_id) as $key => $value) {
110 144 if (is_string($key) && str_contains($key, 'nxt-')) {
111 145 $custom_fields[$key] = $value;
@@ -111,15 +145,14 @@
111 145 $custom_fields[$key] = $value;
112 146 }
113 147 }
114 148 if (!empty($custom_fields)) {
115 - $decoded = json_decode($data, true);
116 - if (is_array($decoded)) {
117 - $decoded['custom_meta'] = $custom_fields;
118 - $data = wp_json_encode($decoded);
119 - }
149 + // On the envelope (an object), so it cannot reshape `content`.
150 + $envelope['custom_meta'] = $custom_fields;
120 151 }
121 152 }
153 +
154 + $data = wp_json_encode($envelope);
122 155
123 156 $args = [
124 157 'data' => $data,
125 158 'post_id' => $post_id,