Skip to content

Commit 690d756

Browse files
committed
feat: implement version switching functionality for the plugin
1 parent cc8faf9 commit 690d756

File tree

1 file changed

+386
-0
lines changed

1 file changed

+386
-0
lines changed
Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
<?php
2+
/**
3+
* Handles version switching functionality for the Code Snippets plugin.
4+
*
5+
* @package Code_Snippets
6+
* @subpackage Settings
7+
*/
8+
9+
namespace Code_Snippets\Settings\VersionSwitch;
10+
11+
use function Code_Snippets\code_snippets;
12+
13+
/**
14+
* Get available plugin versions from WordPress.org repository
15+
*
16+
* @return array Array of version information
17+
*/
18+
function get_available_versions(): array {
19+
$transient_key = 'code_snippets_available_versions';
20+
$versions = get_transient( $transient_key );
21+
22+
if ( false === $versions ) {
23+
$response = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&slug=code-snippets' );
24+
25+
if ( is_wp_error( $response ) ) {
26+
return [];
27+
}
28+
29+
$body = wp_remote_retrieve_body( $response );
30+
$data = json_decode( $body, true );
31+
32+
if ( ! $data || ! isset( $data['versions'] ) ) {
33+
return [];
34+
}
35+
36+
// Filter out 'trunk' and sort versions
37+
$versions = [];
38+
foreach ( $data['versions'] as $version => $download_url ) {
39+
if ( 'trunk' !== $version ) {
40+
$versions[] = [
41+
'version' => $version,
42+
'url' => $download_url,
43+
];
44+
}
45+
}
46+
47+
// Sort versions in descending order
48+
usort( $versions, function( $a, $b ) {
49+
return version_compare( $b['version'], $a['version'] );
50+
});
51+
52+
// Cache for 1 hour
53+
set_transient( $transient_key, $versions, HOUR_IN_SECONDS );
54+
}
55+
56+
return $versions;
57+
}
58+
59+
/**
60+
* Get current plugin version
61+
*
62+
* @return string Current version
63+
*/
64+
function get_current_version(): string {
65+
return defined( 'CODE_SNIPPETS_VERSION' ) ? CODE_SNIPPETS_VERSION : '0.0.0';
66+
}
67+
68+
/**
69+
* Check if a version switch is in progress
70+
*
71+
* @return bool True if switch is in progress
72+
*/
73+
function is_version_switch_in_progress(): bool {
74+
return get_transient( 'code_snippets_version_switch_progress' ) !== false;
75+
}
76+
77+
/**
78+
* Handle version switch request
79+
*
80+
* @param string $target_version Target version to switch to
81+
* @return array Result array with success status and message
82+
*/
83+
function handle_version_switch( string $target_version ): array {
84+
// Check user capabilities
85+
if ( ! current_user_can( 'update_plugins' ) ) {
86+
return [
87+
'success' => false,
88+
'message' => __( 'You do not have permission to update plugins.', 'code-snippets' ),
89+
];
90+
}
91+
92+
// Validate target version
93+
$available_versions = get_available_versions();
94+
$version_exists = false;
95+
$download_url = '';
96+
97+
foreach ( $available_versions as $version_info ) {
98+
if ( $version_info['version'] === $target_version ) {
99+
$version_exists = true;
100+
$download_url = $version_info['url'];
101+
break;
102+
}
103+
}
104+
105+
if ( ! $version_exists ) {
106+
return [
107+
'success' => false,
108+
'message' => __( 'Invalid version specified.', 'code-snippets' ),
109+
];
110+
}
111+
112+
// Check if already on target version
113+
if ( get_current_version() === $target_version ) {
114+
return [
115+
'success' => false,
116+
'message' => __( 'Already on the specified version.', 'code-snippets' ),
117+
];
118+
}
119+
120+
// Set switch in progress
121+
set_transient( 'code_snippets_version_switch_progress', $target_version, 5 * MINUTE_IN_SECONDS );
122+
123+
// Include WordPress upgrade functions
124+
if ( ! function_exists( 'wp_update_plugins' ) ) {
125+
require_once ABSPATH . 'wp-admin/includes/update.php';
126+
}
127+
if ( ! function_exists( 'show_message' ) ) {
128+
require_once ABSPATH . 'wp-admin/includes/misc.php';
129+
}
130+
if ( ! class_exists( 'Plugin_Upgrader' ) ) {
131+
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
132+
}
133+
134+
// Create upgrader instance
135+
$upgrader = new \Plugin_Upgrader( new \Automatic_Upgrader_Skin() );
136+
137+
// Perform the upgrade/downgrade
138+
$result = $upgrader->upgrade( plugin_basename( CODE_SNIPPETS_FILE ), [
139+
'clear_destination' => true,
140+
'overwrite_package' => true,
141+
] );
142+
143+
// Clear progress transient
144+
delete_transient( 'code_snippets_version_switch_progress' );
145+
146+
if ( is_wp_error( $result ) ) {
147+
return [
148+
'success' => false,
149+
'message' => $result->get_error_message(),
150+
];
151+
}
152+
153+
if ( $result ) {
154+
// Clear version cache
155+
delete_transient( 'code_snippets_available_versions' );
156+
157+
return [
158+
'success' => true,
159+
'message' => sprintf(
160+
__( 'Successfully switched to version %s. Please refresh the page to see changes.', 'code-snippets' ),
161+
$target_version
162+
),
163+
];
164+
}
165+
166+
return [
167+
'success' => false,
168+
'message' => __( 'Failed to switch versions. Please try again.', 'code-snippets' ),
169+
];
170+
}
171+
172+
/**
173+
* Render the version switch field
174+
*
175+
* @param array $args Field arguments
176+
*/
177+
function render_version_switch_field( array $args ): void {
178+
$current_version = get_current_version();
179+
$available_versions = get_available_versions();
180+
$is_switching = is_version_switch_in_progress();
181+
182+
?>
183+
<div class="code-snippets-version-switch">
184+
<p>
185+
<strong><?php esc_html_e( 'Current Version:', 'code-snippets' ); ?></strong>
186+
<span class="current-version"><?php echo esc_html( $current_version ); ?></span>
187+
</p>
188+
189+
<?php if ( $is_switching ) : ?>
190+
<div class="notice notice-info inline">
191+
<p><?php esc_html_e( 'Version switch in progress. Please wait...', 'code-snippets' ); ?></p>
192+
</div>
193+
<?php else : ?>
194+
<p>
195+
<label for="target_version">
196+
<?php esc_html_e( 'Switch to Version:', 'code-snippets' ); ?>
197+
</label>
198+
<select id="target_version" name="target_version" <?php disabled( empty( $available_versions ) ); ?>>
199+
<option value=""><?php esc_html_e( 'Select a version...', 'code-snippets' ); ?></option>
200+
<?php foreach ( $available_versions as $version_info ) : ?>
201+
<option value="<?php echo esc_attr( $version_info['version'] ); ?>"
202+
<?php selected( $version_info['version'], $current_version ); ?>>
203+
<?php echo esc_html( $version_info['version'] ); ?>
204+
<?php if ( $version_info['version'] === $current_version ) : ?>
205+
<?php esc_html_e( ' (Current)', 'code-snippets' ); ?>
206+
<?php endif; ?>
207+
</option>
208+
<?php endforeach; ?>
209+
</select>
210+
</p>
211+
212+
<p>
213+
<button type="button" id="switch-version-btn" class="button button-secondary"
214+
<?php disabled( empty( $available_versions ) ); ?>>
215+
<?php esc_html_e( 'Switch Version', 'code-snippets' ); ?>
216+
</button>
217+
</p>
218+
219+
<div id="version-switch-result" class="notice" style="display: none;"></div>
220+
221+
<p class="description">
222+
<?php
223+
esc_html_e( 'Warning: Switching versions may cause compatibility issues. Always backup your site before switching versions. This feature allows you to install different versions of the Code Snippets plugin.', 'code-snippets' );
224+
?>
225+
</p>
226+
<?php endif; ?>
227+
</div>
228+
229+
<script type="text/javascript">
230+
jQuery(document).ready(function($) {
231+
$('#switch-version-btn').on('click', function() {
232+
var targetVersion = $('#target_version').val();
233+
var $button = $(this);
234+
var $result = $('#version-switch-result');
235+
236+
if (!targetVersion) {
237+
$result.removeClass('notice-success notice-error').addClass('notice-warning')
238+
.html('<p><?php esc_html_e( 'Please select a version to switch to.', 'code-snippets' ); ?></p>')
239+
.show();
240+
return;
241+
}
242+
243+
// Disable button and show loading
244+
$button.prop('disabled', true).text('<?php esc_html_e( 'Switching...', 'code-snippets' ); ?>');
245+
$result.removeClass('notice-success notice-error notice-warning').addClass('notice-info')
246+
.html('<p><?php esc_html_e( 'Processing version switch. Please wait...', 'code-snippets' ); ?></p>')
247+
.show();
248+
249+
// Make AJAX request
250+
$.post(ajaxurl, {
251+
action: 'code_snippets_switch_version',
252+
target_version: targetVersion,
253+
nonce: '<?php echo esc_js( wp_create_nonce( 'code_snippets_version_switch' ) ); ?>'
254+
})
255+
.done(function(response) {
256+
if (response.success) {
257+
$result.removeClass('notice-info notice-error').addClass('notice-success')
258+
.html('<p>' + response.data.message + '</p>');
259+
260+
// Refresh page after 3 seconds
261+
setTimeout(function() {
262+
window.location.reload();
263+
}, 3000);
264+
} else {
265+
$result.removeClass('notice-info notice-success').addClass('notice-error')
266+
.html('<p>' + response.data.message + '</p>');
267+
$button.prop('disabled', false).text('<?php esc_html_e( 'Switch Version', 'code-snippets' ); ?>');
268+
}
269+
})
270+
.fail(function() {
271+
$result.removeClass('notice-info notice-success').addClass('notice-error')
272+
.html('<p><?php esc_html_e( 'An error occurred while switching versions. Please try again.', 'code-snippets' ); ?></p>');
273+
$button.prop('disabled', false).text('<?php esc_html_e( 'Switch Version', 'code-snippets' ); ?>');
274+
});
275+
});
276+
});
277+
</script>
278+
<?php
279+
}
280+
281+
/**
282+
* AJAX handler for version switch
283+
*/
284+
function ajax_switch_version(): void {
285+
// Verify nonce
286+
if ( ! wp_verify_nonce( $_POST['nonce'] ?? '', 'code_snippets_version_switch' ) ) {
287+
wp_die( __( 'Security check failed.', 'code-snippets' ) );
288+
}
289+
290+
// Check user capabilities
291+
if ( ! current_user_can( 'update_plugins' ) ) {
292+
wp_send_json_error( [
293+
'message' => __( 'You do not have permission to update plugins.', 'code-snippets' ),
294+
] );
295+
}
296+
297+
$target_version = sanitize_text_field( $_POST['target_version'] ?? '' );
298+
299+
if ( empty( $target_version ) ) {
300+
wp_send_json_error( [
301+
'message' => __( 'No target version specified.', 'code-snippets' ),
302+
] );
303+
}
304+
305+
$result = handle_version_switch( $target_version );
306+
307+
if ( $result['success'] ) {
308+
wp_send_json_success( $result );
309+
} else {
310+
wp_send_json_error( $result );
311+
}
312+
}
313+
314+
// Register AJAX handler
315+
add_action( 'wp_ajax_code_snippets_switch_version', __NAMESPACE__ . '\\ajax_switch_version' );
316+
317+
/**
318+
* Render refresh versions cache button
319+
*
320+
* @param array $args Field arguments
321+
*/
322+
function render_refresh_versions_field( array $args ): void {
323+
?>
324+
<button type="button" id="refresh-versions-btn" class="button button-secondary">
325+
<?php esc_html_e( 'Refresh Available Versions', 'code-snippets' ); ?>
326+
</button>
327+
<p class="description">
328+
<?php esc_html_e( 'Clear the cached list of available versions and fetch the latest from WordPress.org.', 'code-snippets' ); ?>
329+
</p>
330+
331+
<script type="text/javascript">
332+
jQuery(document).ready(function($) {
333+
$('#refresh-versions-btn').on('click', function() {
334+
var $button = $(this);
335+
$button.prop('disabled', true).text('<?php esc_html_e( 'Refreshing...', 'code-snippets' ); ?>');
336+
337+
$.post(ajaxurl, {
338+
action: 'code_snippets_refresh_versions',
339+
nonce: '<?php echo esc_js( wp_create_nonce( 'code_snippets_refresh_versions' ) ); ?>'
340+
})
341+
.done(function(response) {
342+
$button.text('<?php esc_html_e( 'Refreshed!', 'code-snippets' ); ?>');
343+
setTimeout(function() {
344+
$button.prop('disabled', false).text('<?php esc_html_e( 'Refresh Available Versions', 'code-snippets' ); ?>');
345+
// Reload page to show updated versions
346+
window.location.reload();
347+
}, 1000);
348+
})
349+
.fail(function() {
350+
$button.prop('disabled', false).text('<?php esc_html_e( 'Refresh Available Versions', 'code-snippets' ); ?>');
351+
});
352+
});
353+
});
354+
</script>
355+
<?php
356+
}
357+
358+
/**
359+
* AJAX handler for refreshing versions cache
360+
*/
361+
function ajax_refresh_versions(): void {
362+
// Verify nonce
363+
if ( ! wp_verify_nonce( $_POST['nonce'] ?? '', 'code_snippets_refresh_versions' ) ) {
364+
wp_die( __( 'Security check failed.', 'code-snippets' ) );
365+
}
366+
367+
// Check user capabilities
368+
if ( ! current_user_can( 'manage_options' ) ) {
369+
wp_send_json_error( [
370+
'message' => __( 'You do not have permission to manage options.', 'code-snippets' ),
371+
] );
372+
}
373+
374+
// Clear the cache
375+
delete_transient( 'code_snippets_available_versions' );
376+
377+
// Fetch fresh data
378+
get_available_versions();
379+
380+
wp_send_json_success( [
381+
'message' => __( 'Versions cache refreshed successfully.', 'code-snippets' ),
382+
] );
383+
}
384+
385+
// Register AJAX handler
386+
add_action( 'wp_ajax_code_snippets_refresh_versions', __NAMESPACE__ . '\\ajax_refresh_versions' );

0 commit comments

Comments
 (0)