PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.5.0
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.5.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 1.0.17 All 128 releases
wdesignkit / includes / abilities / settings / wdesignkit-rollback.php

wdesignkit-rollback.php in WDesignKit – AI Templates, Widget Builder & MCP Workflow 2.5.0, at includes/abilities/settings/wdesignkit-rollback.php

274 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Abilities: List available rollback versions and execute a WDesignKit plugin rollback.
4 */
5
6 declare(strict_types=1);
7
8 if (!defined('ABSPATH')) {
9 exit();
10 }
11
12 wp_register_ability('wdesignkit/list-rollback-versions', [
13 'label' => __('List WDesignKit Rollback Versions', 'sprout-mcp'),
14 'description' => __(
15 'Fetches the list of stable previous WDesignKit plugin versions available for rollback from wordpress.org. Excludes beta, RC, trunk, and dev versions, and any version equal to or newer than the currently installed version.',
16 'sprout-mcp',
17 ),
18 'category' => 'wdesignkit',
19 'input_schema' => [
20 'type' => 'object',
21 'properties' => (object) [],
22 'additionalProperties' => false,
23 ],
24 'output_schema' => [
25 'type' => 'object',
26 'properties' => [
27 'success' => ['type' => 'boolean'],
28 'message' => ['type' => 'string'],
29 'current_version' => ['type' => 'string'],
30 'versions' => ['type' => 'array', 'items' => ['type' => 'string']],
31 ],
32 ],
33 'execute_callback' => 'wdesignkit_mcp_list_rollback_versions',
34 'permission_callback' => 'sprout_mcp_permission_callback',
35 'meta' => [
36 'show_in_rest' => true,
37 'mcp' => ['public' => true],
38 'annotations' => [
39 'instructions' => implode("\n", [
40 'Lists stable versions older than the current install that can be rolled back to.',
41 'Requires a network request to the wordpress.org plugins API.',
42 'Pass one of the returned version strings to wdesignkit/rollback to downgrade.',
43 ]),
44 'readonly' => true,
45 'destructive' => false,
46 'idempotent' => true,
47 ],
48 ],
49 ]);
50
51 wp_register_ability('wdesignkit/rollback', [
52 'label' => __('Rollback WDesignKit to Previous Version', 'sprout-mcp'),
53 'description' => __(
54 'Downgrades the WDesignKit plugin to a specified previous stable version using the WordPress Plugin Upgrader. The plugin is automatically re-activated after installation. This replaces the current plugin files and cannot be undone without a further rollback or update. Requires confirm: true.',
55 'sprout-mcp',
56 ),
57 'category' => 'wdesignkit',
58 'input_schema' => [
59 'type' => 'object',
60 'properties' => [
61 'version' => [
62 'type' => 'string',
63 'description' => 'Version string to roll back to (from wdesignkit/list-rollback-versions).',
64 ],
65 'confirm' => [
66 'type' => 'boolean',
67 'description' => 'Must be true to execute. Omit or false for a dry-run preview.',
68 ],
69 ],
70 'required' => ['version'],
71 'additionalProperties' => false,
72 ],
73 'output_schema' => [
74 'type' => 'object',
75 'properties' => [
76 'success' => ['type' => 'boolean'],
77 'message' => ['type' => 'string'],
78 'version' => ['type' => 'string'],
79 'dry_run' => ['type' => 'boolean'],
80 'warning' => ['type' => ['string', 'null']],
81 ],
82 ],
83 'execute_callback' => 'wdesignkit_mcp_rollback',
84 'permission_callback' => 'sprout_mcp_permission_callback',
85 'meta' => [
86 'show_in_rest' => true,
87 'mcp' => ['public' => true],
88 'annotations' => [
89 'instructions' => implode("\n", [
90 'Rolls back WDesignKit to a previous version. Destructive — replaces plugin files.',
91 'Always call wdesignkit/list-rollback-versions first to validate the version string.',
92 'confirm: true is required to execute; without it the call returns a dry-run preview.',
93 'The plugin is re-activated automatically. The MCP connection may need to be re-established after rollback.',
94 ]),
95 'readonly' => false,
96 'destructive' => true,
97 'idempotent' => false,
98 ],
99 ],
100 ]);
101
102 function wdesignkit_mcp_list_rollback_versions(array $input): array {
103 if (!defined('WDKIT_VERSION')) {
104 return ['success' => false, 'message' => 'WDesignKit plugin is not active.', 'versions' => [], 'current_version' => ''];
105 }
106
107 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
108
109 $plugin_info = plugins_api('plugin_information', ['slug' => 'wdesignkit']);
110
111 if (is_wp_error($plugin_info)) {
112 return [
113 'success' => false,
114 'message' => $plugin_info->get_error_message(),
115 'current_version' => WDKIT_VERSION,
116 'versions' => [],
117 ];
118 }
119
120 if (empty($plugin_info->versions) || !is_array($plugin_info->versions)) {
121 return [
122 'success' => false,
123 'message' => 'No version data returned from wordpress.org.',
124 'current_version' => WDKIT_VERSION,
125 'versions' => [],
126 ];
127 }
128
129 $versions = [];
130 foreach ($plugin_info->versions as $version => $download_link) {
131 $is_valid = !preg_match('/(beta|rc|trunk|dev)/i', strtolower($version));
132 $is_valid = apply_filters('wdkit_check_rollback_version', $is_valid, strtolower($version));
133
134 if (!$is_valid || version_compare($version, WDKIT_VERSION, '>=')) {
135 continue;
136 }
137
138 $versions[] = $version;
139 }
140
141 // Sort descending by semantic version so multi-digit patch numbers are ordered correctly
142 // (e.g. "2.2.10" > "2.2.9" > "2.2.2"). krsort / string comparison incorrectly places
143 // "2.2.10" after "2.2.2" because "10" < "2" lexicographically.
144 usort($versions, static function (string $a, string $b): int {
145 return version_compare($b, $a);
146 });
147
148 return [
149 'success' => true,
150 'message' => count($versions) . ' rollback version(s) available.',
151 'current_version' => WDKIT_VERSION,
152 'versions' => $versions,
153 ];
154 }
155
156 function wdesignkit_mcp_rollback(array $input): array {
157 if (!defined('WDKIT_VERSION') || !defined('WDKIT_PBNAME')) {
158 return ['success' => false, 'message' => 'WDesignKit plugin is not active.', 'dry_run' => false];
159 }
160
161 $version = sanitize_text_field((string) ($input['version'] ?? ''));
162 $confirm = (bool) ($input['confirm'] ?? false);
163
164 if ($version === '') {
165 return ['success' => false, 'message' => 'version is required.', 'dry_run' => false];
166 }
167
168 // Guard 1: same-version or newer — reject before any network call.
169 if (version_compare($version, WDKIT_VERSION, '=')) {
170 return [
171 'success' => false,
172 'message' => "Cannot roll back to the currently installed version {$version}.",
173 'version' => $version,
174 'dry_run' => !$confirm,
175 ];
176 }
177 if (version_compare($version, WDKIT_VERSION, '>')) {
178 return [
179 'success' => false,
180 'message' => "Version {$version} is newer than the installed version " . WDKIT_VERSION . ". Use the plugin updater instead.",
181 'version' => $version,
182 'dry_run' => !$confirm,
183 ];
184 }
185
186 // Guard 2: validate against the available rollback list from wordpress.org.
187 // Runs for BOTH dry-run and execute so a "would roll back to X" preview is
188 // never shown for a version that does not exist in the release history.
189 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
190
191 $plugin_info = plugins_api('plugin_information', ['slug' => 'wdesignkit']);
192
193 if (is_wp_error($plugin_info) || empty($plugin_info->versions) || !is_array($plugin_info->versions)) {
194 return [
195 'success' => false,
196 'message' => 'Could not fetch version list from wordpress.org.',
197 'dry_run' => !$confirm,
198 ];
199 }
200
201 $valid_versions = [];
202 foreach ($plugin_info->versions as $v => $dl) {
203 $is_valid = !preg_match('/(beta|rc|trunk|dev)/i', strtolower($v));
204 $is_valid = apply_filters('wdkit_check_rollback_version', $is_valid, strtolower($v));
205 if ($is_valid && version_compare($v, WDKIT_VERSION, '<')) {
206 $valid_versions[] = $v;
207 }
208 }
209
210 if (!in_array($version, $valid_versions, true)) {
211 return [
212 'success' => false,
213 'message' => "Version '{$version}' is not available for rollback. Call wdesignkit/list-rollback-versions to see available versions.",
214 'version' => $version,
215 'dry_run' => !$confirm,
216 ];
217 }
218
219 // Version is valid. Return a dry-run preview (success:true) if not confirmed.
220 if (!$confirm) {
221 return [
222 'success' => true,
223 'message' => "Dry run: would roll back WDesignKit from " . WDKIT_VERSION . " to {$version}. Pass confirm: true to execute.",
224 'version' => $version,
225 'dry_run' => true,
226 ];
227 }
228
229 // Execute — $plugin_info already fetched and validated above.
230 $plugin_slug = basename(WDKIT_PBNAME, '.php');
231 $package_url = sprintf('https://downloads.wordpress.org/plugin/%s.%s.zip', $plugin_slug, $version);
232
233 $plugin_obj = new \stdClass();
234 $plugin_obj->new_version = $version;
235 $plugin_obj->slug = $plugin_slug;
236 $plugin_obj->package = $package_url;
237 $plugin_obj->url = 'https://wdesignkit.com/';
238
239 $update_transient = get_site_transient('update_plugins');
240 if (!is_object($update_transient)) {
241 $update_transient = new \stdClass();
242 }
243 $update_transient->response[WDKIT_PBNAME] = $plugin_obj;
244 set_site_transient('update_plugins', $update_transient);
245
246 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
247
248 $upgrader = new \Plugin_Upgrader(new \WP_Ajax_Upgrader_Skin());
249 $result = $upgrader->upgrade(WDKIT_PBNAME);
250
251 if (is_wp_error($result)) {
252 return [
253 'success' => false,
254 'message' => $result->get_error_message(),
255 'dry_run' => false,
256 ];
257 }
258
259 activate_plugin(WDKIT_PBNAME);
260
261 return [
262 'success' => true,
263 'message' => "WDesignKit rolled back to version {$version} and re-activated.",
264 'version' => $version,
265 'dry_run' => false,
266 // The Plugin Upgrader replaces all plugin PHP files on disk. The currently running
267 // MCP process loaded the old plugin code at boot — its handler registry, class
268 // definitions, and ability list all reflect the pre-rollback version and will not
269 // automatically reload. Every subsequent tool call will fail with "Tool execution
270 // failed" until the MCP client reconnects (which triggers a fresh PHP bootstrap).
271 'warning' => 'MCP server reconnection required: the plugin files have been replaced. Reconnect the MCP client before making any further tool calls.',
272 ];
273 }
274