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 / settings / wdesignkit-rollback.php

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

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