PluginProbe
Manage – Centralized site maintenance and monitoring / trunk
Manage – Centralized site maintenance and monitoring vtrunk
1.0.9 1.0.8 1.0.7 1.0.6 1.0.5 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4
manage / modules / api / components / plugins.php

plugins.php in Manage – Centralized site maintenance and monitoring trunk, at modules/api/components/plugins.php

940 lines 26.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Manage\Modules\Api\Components;
3
4 use Manage\Modules\Api\Classes\Route;
5 use Manage\Modules\Api\Classes\Translation_Updater;
6 use Manage\Modules\Api\Classes\Temp_Backup;
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit; // Exit if accessed directly.
10 }
11
12 class Plugins extends Route {
13
14 public function register_routes() {
15 register_rest_route( static::NAMESPACE, '/plugins/install', [
16 'methods' => 'POST',
17 'permission_callback' => [ $this, 'token_authentication' ],
18 'callback' => [ $this, 'install_plugin' ],
19 'args' => [
20 'type' => [
21 'required' => true,
22 'type' => 'string',
23 'description' => 'The type of installation: "slug" for WordPress.org repository or "url" for direct URL.',
24 'enum' => [ 'slug', 'url' ],
25 'sanitize_callback' => 'sanitize_text_field',
26 ],
27 'source' => [
28 'required' => true,
29 'type' => 'string',
30 'description' => 'The plugin slug (for type "slug") or URL (for type "url").',
31 'sanitize_callback' => static function ( $value, $request ) {
32 $type = $request->get_param( 'type' );
33
34 return 'slug' === $type ? sanitize_text_field( $value ) : esc_url_raw( $value );
35 },
36 ],
37 'activate' => [
38 'required' => false,
39 'type' => 'boolean',
40 'default' => false,
41 'description' => 'If true, activate the plugin after a successful install.',
42 'sanitize_callback' => 'rest_sanitize_boolean',
43 ],
44 ],
45 ] );
46
47 register_rest_route( static::NAMESPACE, '/plugins/activate', [
48 'methods' => 'POST',
49 'permission_callback' => [ $this, 'token_authentication' ],
50 'callback' => [ $this, 'activate_plugin' ],
51 'args' => [
52 'slug' => [
53 'required' => true,
54 'type' => 'string',
55 'description' => 'The slug of the plugin to activate.',
56 ],
57 'network_activate' => [
58 'required' => false,
59 'type' => 'boolean',
60 'default' => false,
61 'description' => 'Whether to activate the plugin for all sites in a multisite network.',
62 ],
63 ],
64 ] );
65
66 register_rest_route( static::NAMESPACE, '/plugins/deactivate', [
67 'methods' => 'POST',
68 'permission_callback' => [ $this, 'token_authentication' ],
69 'callback' => [ $this, 'deactivate_plugin' ],
70 'args' => [
71 'slug' => [
72 'required' => true,
73 'type' => 'string',
74 'description' => 'The slug of the plugin to deactivate.',
75 ],
76 ],
77 ] );
78
79 register_rest_route( static::NAMESPACE, '/plugins/uninstall', [
80 'methods' => 'POST',
81 'permission_callback' => [ $this, 'token_authentication' ],
82 'callback' => [ $this, 'uninstall_plugin' ],
83 'args' => [
84 'slug' => [
85 'required' => true,
86 'type' => 'string',
87 'description' => 'The slug of the plugin to uninstall.',
88 ],
89 ],
90 ] );
91
92 register_rest_route( static::NAMESPACE, '/plugins/update', [
93 'methods' => 'POST',
94 'permission_callback' => [ $this, 'token_authentication' ],
95 'callback' => [ $this, 'update_plugin' ],
96 'args' => [
97 'slug' => [
98 'required' => true,
99 'type' => 'string',
100 'description' => 'The slug of the plugin to update.',
101 ],
102 ],
103 ] );
104
105 register_rest_route( static::NAMESPACE, '/plugins/rollback', [
106 'methods' => 'GET',
107 'permission_callback' => [ $this, 'token_authentication' ],
108 'callback' => [ $this, 'get_rollback_versions' ],
109 'args' => [
110 'slug' => [
111 'required' => true,
112 'type' => 'string',
113 'description' => 'The slug of the plugin to get rollback versions for.',
114 ],
115 ],
116 ] );
117
118 register_rest_route( static::NAMESPACE, '/plugins/rollback', [
119 'methods' => 'POST',
120 'permission_callback' => [ $this, 'token_authentication' ],
121 'callback' => [ $this, 'rollback_plugin' ],
122 'args' => [
123 'slug' => [
124 'required' => true,
125 'type' => 'string',
126 'description' => 'The slug of the plugin to rollback.',
127 ],
128 'version' => [
129 'required' => true,
130 'type' => 'string',
131 'description' => 'The version to rollback to.',
132 ],
133 ],
134 ] );
135
136 register_rest_route( static::NAMESPACE, '/plugins/update-translations', [
137 'methods' => 'POST',
138 'permission_callback' => [ $this, 'token_authentication' ],
139 'callback' => [ $this, 'update_all_translations' ],
140 'args' => [],
141 ] );
142
143 register_rest_route( static::NAMESPACE, '/plugins/update-db', [
144 'methods' => 'POST',
145 'permission_callback' => [ $this, 'token_authentication' ],
146 'callback' => [ $this, 'update_plugin_db' ],
147 'args' => [
148 'slug' => [
149 'required' => true,
150 'type' => 'string',
151 'description' => 'The slug of the plugin to update database for.',
152 ],
153 ],
154 ] );
155
156 register_rest_route( static::NAMESPACE, '/plugins/temp-backup', [
157 'methods' => 'POST',
158 'permission_callback' => [ $this, 'token_authentication' ],
159 'callback' => [ $this, 'create_temp_backup' ],
160 'args' => [
161 'slug' => [
162 'required' => true,
163 'type' => 'string',
164 'description' => 'The slug of the plugin to create temp backup for.',
165 ],
166 ],
167 ] );
168
169 register_rest_route( static::NAMESPACE, '/plugins/changelog', [
170 'methods' => 'GET',
171 'permission_callback' => [ $this, 'token_authentication' ],
172 'callback' => [ $this, 'get_plugin_changelog' ],
173 'args' => [
174 'slug' => [
175 'required' => true,
176 'type' => 'string',
177 'description' => 'The plugin file slug (e.g., hello-dolly/hello.php).',
178 ],
179 ],
180 ] );
181 }
182
183 public function get_plugin_changelog( $request ) {
184 $plugin_slug = $request->get_param( 'slug' );
185
186 if ( empty( $plugin_slug ) ) {
187 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
188 }
189
190 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
191
192 $dir_slug = dirname( $plugin_slug );
193
194 $api = plugins_api(
195 'plugin_information',
196 [
197 'slug' => $dir_slug,
198 'fields' => [
199 'sections' => true,
200 'description' => false,
201 'tested' => false,
202 'requires' => false,
203 'rating' => false,
204 'ratings' => false,
205 'downloaded' => false,
206 'downloadlink' => false,
207 'last_updated' => false,
208 'homepage' => false,
209 'tags' => false,
210 'compatibility' => false,
211 ],
212 ]
213 );
214
215 if ( is_wp_error( $api ) ) {
216 if ( str_contains( $api->get_error_message(), 'Plugin not found.' ) ) {
217 $api->add_data( [ 'status' => \WP_Http::NOT_FOUND ] );
218 } else {
219 $api->add_data( [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
220 }
221
222 return $api;
223 }
224
225 $changelog = '';
226 if ( isset( $api->sections ) && is_array( $api->sections ) && isset( $api->sections['changelog'] ) ) {
227 $changelog = (string) $api->sections['changelog'];
228 }
229
230 return rest_ensure_response( [
231 'status' => 'success',
232 'data' => [
233 'changelog' => $changelog,
234 ],
235 ] );
236 }
237
238 public function install_plugin( $request ) {
239 $type = $request->get_param( 'type' );
240 $source = $request->get_param( 'source' );
241
242 if ( empty( $type ) || empty( $source ) ) {
243 return new \WP_Error( 'missing_parameters', 'Both type and source parameters are required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
244 }
245
246 if ( ! in_array( $type, [ 'slug', 'url' ], true ) ) {
247 return new \WP_Error( 'invalid_type', 'Type must be either "slug" or "url".', [ 'status' => \WP_Http::BAD_REQUEST ] );
248 }
249
250 require_once ABSPATH . 'wp-admin/includes/file.php';
251 require_once ABSPATH . 'wp-admin/includes/plugin.php';
252 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
253 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
254
255 $skin = new \WP_Ajax_Upgrader_Skin();
256 $upgrader = new \Plugin_Upgrader( $skin );
257
258 if ( 'url' === $type ) {
259 $package_url = $this->get_package_url_from_plugin_url( $source );
260 } else {
261 $package_url = $this->get_package_url_from_plugin_slug( $source );
262 }
263
264 if ( is_wp_error( $package_url ) ) {
265 return $package_url;
266 }
267
268 $result = $upgrader->install( $package_url );
269
270 if ( is_wp_error( $result ) ) {
271 $result->add_data( [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
272
273 return $result;
274 }
275
276 if ( is_wp_error( $skin->result ) ) {
277 $error_status = \WP_Http::INTERNAL_SERVER_ERROR;
278 if ( 'folder_exists' === $skin->result->get_error_code() ) {
279 $error_status = \WP_Http::BAD_REQUEST;
280 }
281
282 $skin->result->add_data( [ 'status' => $error_status ] );
283
284 return $skin->result;
285 }
286
287 if ( $skin->get_errors()->has_errors() ) {
288 $error = $skin->get_errors();
289 $error->add_data( [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
290
291 return $error;
292 }
293
294 $file = $upgrader->plugin_info();
295
296 if ( ! $file ) {
297 return new \WP_Error(
298 'unable_to_determine_installed_plugin',
299 'Unable to determine what plugin was installed.',
300 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
301 );
302 }
303
304 $response_payload = [
305 'status' => 'success',
306 'message' => 'Plugin installed successfully.',
307 ];
308
309 if ( $request->get_param( 'activate' ) ) {
310 $activation_result = $this->activate_installed_plugin( $file, false );
311 $response_payload['activated'] = ! is_wp_error( $activation_result );
312 }
313
314 return rest_ensure_response( $response_payload );
315 }
316
317 private function get_package_url_from_plugin_url( string $plugin_url ) {
318 if ( ! filter_var( $plugin_url, FILTER_VALIDATE_URL ) ) {
319 return new \WP_Error( 'invalid_url', 'Invalid plugin URL provided.', [ 'status' => \WP_Http::BAD_REQUEST ] );
320 }
321
322 $parsed_url = wp_parse_url( $plugin_url );
323 if ( ! $parsed_url || ( 'https' !== $parsed_url['scheme'] && 'http' !== $parsed_url['scheme'] ) ) {
324 return new \WP_Error( 'invalid_url_scheme', 'Plugin URL must use HTTP or HTTPS protocol.', [ 'status' => \WP_Http::BAD_REQUEST ] );
325 }
326
327 return $plugin_url;
328 }
329
330 private function get_package_url_from_plugin_slug( string $plugin_slug ) {
331 $api = plugins_api( 'plugin_information', [
332 'slug' => $plugin_slug,
333 'fields' => [
334 'sections' => false,
335 'language_packs' => true,
336 ],
337 ] );
338
339 if ( is_wp_error( $api ) ) {
340 if ( str_contains( $api->get_error_message(), 'Plugin not found.' ) ) {
341 $api->add_data( [ 'status' => \WP_Http::NOT_FOUND ] );
342 } else {
343 $api->add_data( [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
344 }
345
346 return $api;
347 }
348
349 $status = install_plugin_install_status( $api );
350
351 if ( 'install' !== $status['status'] ) {
352 return new \WP_Error( 'already_installed', 'Plugin already installed.', [ 'status' => \WP_Http::BAD_REQUEST ] );
353 }
354
355 return $api->download_link;
356 }
357
358 public function activate_plugin( $request ) {
359 $plugin_slug = $request->get_param( 'slug' );
360 $network_activate = (bool) $request->get_param( 'network_activate' );
361
362 if ( empty( $plugin_slug ) ) {
363 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
364 }
365
366 $result = $this->activate_installed_plugin( $plugin_slug, $network_activate );
367
368 if ( is_wp_error( $result ) ) {
369 return $result;
370 }
371
372 return rest_ensure_response( [
373 'status' => 'success',
374 'message' => 'Plugin activated successfully.',
375 ] );
376 }
377
378 private function activate_installed_plugin( string $plugin_slug, bool $network_activate ) {
379 require_once ABSPATH . 'wp-admin/includes/plugin.php';
380
381 $plugin_data = $this->get_plugin_data( $plugin_slug );
382
383 if ( is_wp_error( $plugin_data ) ) {
384 return $plugin_data;
385 }
386
387 if ( is_multisite() && ! $network_activate && is_network_only_plugin( $plugin_slug ) ) {
388 return new \WP_Error( 'rest_network_only_plugin', 'Network only plugin must be network activated.', [ 'status' => \WP_Http::BAD_REQUEST ] );
389 }
390
391 $activated = activate_plugin( $plugin_slug, '', $network_activate );
392
393 if ( is_wp_error( $activated ) ) {
394 $activated->add_data( [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
395
396 return $activated;
397 }
398
399 return true;
400 }
401
402 private function get_plugin_data( $plugin_slug ) {
403 $plugins = get_plugins();
404
405 if ( ! isset( $plugins[ $plugin_slug ] ) ) {
406 return new \WP_Error( 'rest_plugin_not_found', 'Plugin not found.', [ 'status' => \WP_Http::NOT_FOUND ] );
407 }
408
409 $data = $plugins[ $plugin_slug ];
410 $data['_file'] = $plugin_slug;
411
412 return $data;
413 }
414
415 private function get_plugin_status( $plugin ): string {
416 if ( is_plugin_active_for_network( $plugin ) ) {
417 return 'network-active';
418 }
419
420 if ( is_plugin_active( $plugin ) ) {
421 return 'active';
422 }
423
424 return 'inactive';
425 }
426
427 public function deactivate_plugin( $request ) {
428 $plugin_slug = $request->get_param( 'slug' );
429
430 if ( empty( $plugin_slug ) ) {
431 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
432 }
433
434 require_once ABSPATH . 'wp-admin/includes/plugin.php';
435
436 deactivate_plugins( $plugin_slug, false );
437
438 return rest_ensure_response( [
439 'status' => 'success',
440 'message' => 'Plugin deactivated successfully.',
441 ] );
442 }
443
444 public function uninstall_plugin( $request ) {
445 $plugin_slug = $request->get_param( 'slug' );
446
447 if ( empty( $plugin_slug ) ) {
448 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
449 }
450
451 require_once ABSPATH . 'wp-admin/includes/file.php';
452 require_once ABSPATH . 'wp-admin/includes/plugin.php';
453
454 $plugin_data = $this->get_plugin_data( $plugin_slug );
455
456 if ( is_wp_error( $plugin_data ) ) {
457 return $plugin_data;
458 }
459
460 if ( is_plugin_active( $plugin_slug ) ) {
461 return new \WP_Error( 'rest_cannot_delete_active_plugin', 'Cannot delete an active plugin. Please deactivate it first.', [ 'status' => \WP_Http::BAD_REQUEST ] );
462 }
463
464 $deleted = delete_plugins( [ $plugin_slug ] );
465
466 if ( is_wp_error( $deleted ) ) {
467 $deleted->add_data( [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
468
469 return $deleted;
470 }
471
472 return rest_ensure_response( [
473 'status' => 'success',
474 'message' => 'Plugin uninstalled successfully.',
475 ] );
476 }
477
478 public function get_rollback_versions( $request ) {
479 $plugin_slug = $request->get_param( 'slug' );
480
481 if ( empty( $plugin_slug ) ) {
482 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
483 }
484
485 require_once ABSPATH . 'wp-admin/includes/plugin.php';
486 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
487
488 $plugin_data = $this->get_plugin_data( $plugin_slug );
489
490 if ( is_wp_error( $plugin_data ) ) {
491 return $plugin_data;
492 }
493
494 $api = plugins_api(
495 'plugin_information',
496 [
497 'slug' => dirname( $plugin_slug ),
498 'fields' => [
499 'versions' => true,
500 'sections' => false,
501 'description' => false,
502 'tested' => false,
503 'requires' => false,
504 'rating' => false,
505 'ratings' => false,
506 'downloaded' => false,
507 'downloadlink' => false,
508 'last_updated' => false,
509 'homepage' => false,
510 'tags' => false,
511 'compatibility' => false,
512 ],
513 ]
514 );
515
516 if ( is_wp_error( $api ) ) {
517 if ( str_contains( $api->get_error_message(), 'Plugin not found.' ) ) {
518 $api->add_data( [ 'status' => \WP_Http::NOT_FOUND ] );
519 } else {
520 $api->add_data( [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
521 }
522
523 return $api;
524 }
525
526 $versions = [];
527
528 if ( isset( $api->versions ) && is_array( $api->versions ) ) {
529 unset( $api->versions['trunk'] );
530
531 foreach ( $api->versions as $version => $download_link ) {
532 $versions[] = $version;
533 }
534
535 usort( $versions, 'version_compare' );
536 $versions = array_reverse( $versions );
537
538 $maximum_versions = 30;
539 $versions = array_slice( $versions, 0, $maximum_versions );
540 }
541
542 $plugin_dir_name = dirname( $plugin_slug );
543 $has_temp_backup = Temp_Backup::backup_exists( $plugin_dir_name, Temp_Backup::PLUGINS );
544
545 return rest_ensure_response( [
546 'status' => 'success',
547 'data' => [
548 'versions' => $versions,
549 'has_temp_backup' => $has_temp_backup,
550 ],
551 ] );
552 }
553
554 public function rollback_plugin( $request ) {
555 $plugin_slug = $request->get_param( 'slug' );
556 $version = $request->get_param( 'version' );
557
558 if ( empty( $plugin_slug ) ) {
559 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
560 }
561
562 if ( empty( $version ) ) {
563 return new \WP_Error( 'missing_version', 'Version is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
564 }
565
566 require_once ABSPATH . 'wp-admin/includes/file.php';
567 require_once ABSPATH . 'wp-admin/includes/plugin.php';
568 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
569 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
570
571 $plugin_data = $this->get_plugin_data( $plugin_slug );
572
573 if ( is_wp_error( $plugin_data ) ) {
574 return $plugin_data;
575 }
576
577 $was_active = is_plugin_active( $plugin_slug );
578 $was_network_active = is_multisite() && is_plugin_active_for_network( $plugin_slug );
579
580 if ( $was_active || $was_network_active ) {
581 deactivate_plugins( $plugin_slug, false, $was_network_active );
582 }
583
584 if ( 'temp-backup' === $version ) {
585 $plugin_dir_name = dirname( $plugin_slug );
586 $restore_result = Temp_Backup::restore_plugin_backup( $plugin_dir_name );
587
588 if ( $was_active || $was_network_active ) {
589 activate_plugin( $plugin_slug, '', $was_network_active );
590 }
591
592 if ( is_wp_error( $restore_result ) ) {
593 return $restore_result;
594 }
595
596 return rest_ensure_response( [
597 'status' => 'success',
598 'message' => 'Plugin restored successfully from temp backup.',
599 'restored_from' => 'temp_backup',
600 ] );
601 }
602
603 $download_url = sprintf(
604 'https://downloads.wordpress.org/plugin/%s.%s.zip',
605 dirname( $plugin_slug ),
606 $version
607 );
608
609 $skin = new \WP_Ajax_Upgrader_Skin();
610 $upgrader = new \Plugin_Upgrader( $skin );
611 $result = $upgrader->install( $download_url, [
612 'overwrite_package' => true,
613 ] );
614
615 if ( is_wp_error( $result ) ) {
616 return new \WP_Error(
617 'rollback_failed',
618 'Failed to rollback plugin: ' . $result->get_error_message(),
619 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
620 );
621 }
622
623 if ( is_wp_error( $skin->result ) ) {
624 return new \WP_Error(
625 'rollback_failed',
626 'Failed to rollback plugin: ' . $skin->result->get_error_message(),
627 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
628 );
629 }
630
631 if ( $skin->get_errors()->has_errors() ) {
632 return new \WP_Error(
633 'rollback_failed',
634 'Failed to rollback plugin: ' . $skin->get_error_messages(),
635 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
636 );
637 }
638
639 if ( ! $result ) {
640 return new \WP_Error(
641 'rollback_failed',
642 'Failed to rollback plugin for an unknown reason.',
643 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
644 );
645 }
646
647 if ( $was_active || $was_network_active ) {
648 activate_plugin( $plugin_slug, '', $was_network_active );
649 }
650
651 return rest_ensure_response( [
652 'status' => 'success',
653 'message' => 'Plugin rolled back successfully to version ' . $version,
654 'restored_from' => 'wordpress_org',
655 ] );
656 }
657
658 public function update_plugin( $request ) {
659 $plugin_slug = $request->get_param( 'slug' );
660
661 if ( empty( $plugin_slug ) ) {
662 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
663 }
664
665 require_once ABSPATH . 'wp-admin/includes/file.php';
666 require_once ABSPATH . 'wp-admin/includes/plugin.php';
667 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
668 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
669 require_once ABSPATH . 'wp-admin/includes/update.php';
670
671 $plugin_data = $this->get_plugin_data( $plugin_slug );
672
673 if ( is_wp_error( $plugin_data ) ) {
674 return $plugin_data;
675 }
676
677 // Check if there's an update available
678 wp_update_plugins();
679 $current = get_site_transient( 'update_plugins' );
680
681 if ( ! isset( $current->response[ $plugin_slug ] ) ) {
682 return new \WP_Error(
683 'no_update_available',
684 'No update available for this plugin.',
685 [ 'status' => \WP_Http::BAD_REQUEST ]
686 );
687 }
688
689 // Match wp_ajax_update_plugin(): bulk_upgrade does not deactivate active plugins.
690 $skin = new \WP_Ajax_Upgrader_Skin();
691 $upgrader = new \Plugin_Upgrader( $skin );
692 $result = $upgrader->bulk_upgrade( [ $plugin_slug ] );
693
694 if ( is_wp_error( $skin->result ) ) {
695 return new \WP_Error(
696 'update_failed',
697 'Failed to update plugin: ' . $skin->result->get_error_message(),
698 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
699 );
700 }
701
702 if ( $skin->get_errors()->has_errors() ) {
703 return new \WP_Error(
704 'update_failed',
705 'Failed to update plugin: ' . $skin->get_error_messages(),
706 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
707 );
708 }
709
710 if ( false === $result ) {
711 return new \WP_Error(
712 'update_failed',
713 'Failed to update plugin: unable to connect to the filesystem.',
714 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
715 );
716 }
717
718 $plugin_result = $result[ $plugin_slug ] ?? null;
719
720 if ( is_wp_error( $plugin_result ) ) {
721 return new \WP_Error(
722 'update_failed',
723 'Failed to update plugin: ' . $plugin_result->get_error_message(),
724 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
725 );
726 }
727
728 if ( true === $plugin_result ) {
729 return new \WP_Error(
730 'no_update_available',
731 'No update available for this plugin.',
732 [ 'status' => \WP_Http::BAD_REQUEST ]
733 );
734 }
735
736 if ( empty( $plugin_result ) ) {
737 return new \WP_Error(
738 'update_failed',
739 'Failed to update plugin for an unknown reason.',
740 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
741 );
742 }
743
744 $plugin_data = $this->get_plugin_data( $plugin_slug );
745 $new_version = $plugin_data['Version'];
746
747 return rest_ensure_response( [
748 'status' => 'success',
749 'message' => 'Plugin updated successfully to version ' . $new_version,
750 ] );
751 }
752
753 public function update_all_translations( $request ) {
754 return Translation_Updater::update_translations( 'plugin' );
755 }
756
757 public function update_plugin_db( $request ) {
758 $plugin_slug = $request->get_param( 'slug' );
759
760 if ( empty( $plugin_slug ) ) {
761 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
762 }
763
764 if ( ! is_plugin_active( $plugin_slug ) ) {
765 return new \WP_Error(
766 'plugin_not_active',
767 'Plugin must be active to update its database.',
768 [ 'status' => \WP_Http::BAD_REQUEST ]
769 );
770 }
771
772 if ( false !== strpos( $plugin_slug, 'woocommerce' ) ) {
773 return $this->update_woocommerce_db();
774 }
775
776 if ( false !== strpos( $plugin_slug, 'elementor' ) ) {
777 return $this->update_elementor_db( 'Elementor', '\Elementor\Core\Upgrade\Manager' );
778 }
779
780 if ( false !== strpos( $plugin_slug, 'elementor-pro' ) ) {
781 return $this->update_elementor_db( 'Elementor Pro', '\ElementorPro\Core\Upgrade\Manager' );
782 }
783
784 return new \WP_Error( 'no_db_update_config', 'No database update configuration found for this plugin.', [ 'status' => \WP_Http::BAD_REQUEST ] );
785 }
786
787 private function update_woocommerce_db() {
788 if ( ! class_exists( 'WooCommerce' ) ) {
789 return new \WP_Error(
790 'woocommerce_not_found',
791 'WooCommerce is not installed or active.',
792 [ 'status' => \WP_Http::BAD_REQUEST ]
793 );
794 }
795
796 $current_db_version = get_option( 'woocommerce_db_version', null );
797 $update_db_version = WC()->version;
798
799 if ( version_compare( $current_db_version, $update_db_version, '>=' ) ) {
800 return rest_ensure_response( [
801 'status' => 'success',
802 'message' => 'WooCommerce database is already up to date.',
803 'current_version' => $current_db_version,
804 ] );
805 }
806
807 try {
808 if ( ! class_exists( 'WC_Install' ) ) {
809 require_once WC_ABSPATH . 'includes/class-wc-install.php';
810 }
811
812 \WC_Install::run_manual_database_update();
813
814 return rest_ensure_response( [
815 'status' => 'success',
816 'message' => 'WooCommerce database updated successfully.',
817 'previous_version' => $current_db_version,
818 'new_version' => $update_db_version,
819 ] );
820 } catch ( \Exception $e ) {
821 return new \WP_Error(
822 'wc_db_update_failed',
823 'Failed to update WooCommerce database: ' . $e->getMessage(),
824 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
825 );
826 }
827 }
828
829 private function update_elementor_db( $plugin_name, $manager_class ) {
830 if ( ! class_exists( 'Elementor\Plugin' ) ) {
831 return new \WP_Error( 'elementor_not_found', 'Elementor is not installed or active.', [ 'status' => \WP_Http::BAD_REQUEST ] );
832 }
833
834 try {
835 if ( ! class_exists( $manager_class ) ) {
836 return new \WP_Error(
837 'elementor_upgrade_manager_not_available',
838 $plugin_name . ' upgrade manager class not available: ' . $manager_class,
839 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
840 );
841 }
842
843 /** @var \Elementor\Core\Upgrade\Manager $manager */
844 $manager = new $manager_class();
845
846 $current_version = $manager->get_current_version();
847 $new_version = $manager->get_new_version();
848
849 if ( ! $manager->should_upgrade() ) {
850 return rest_ensure_response( [
851 'status' => 'success',
852 'message' => $plugin_name . ' database is already up to date.',
853 'current_version' => $current_version,
854 'new_version' => $new_version,
855 ] );
856 }
857
858 $updater = $manager->get_task_runner();
859
860 if ( $updater->is_process_locked() ) {
861 return new \WP_Error(
862 'elementor_upgrade_in_progress',
863 $plugin_name . ' database upgrade is already in progress.',
864 [ 'status' => \WP_Http::CONFLICT ]
865 );
866 }
867
868 $callbacks = $manager->get_upgrade_callbacks();
869 $did_tasks = false;
870
871 if ( ! empty( $callbacks ) ) {
872 if ( isset( \Elementor\Plugin::$instance->logger ) ) {
873 \Elementor\Plugin::$instance->logger->get_logger()->info( 'Update DB has been started', [
874 'meta' => [
875 'plugin' => $manager->get_plugin_label(),
876 'from' => $current_version,
877 'to' => $new_version,
878 ],
879 ] );
880 }
881
882 $updater->handle_immediately( $callbacks );
883 $did_tasks = true;
884 }
885
886 $manager->on_runner_complete( $did_tasks );
887
888 if ( class_exists( '\Elementor\Plugin' ) && method_exists( '\Elementor\Plugin', 'instance' ) ) {
889 $elementor = \Elementor\Plugin::instance();
890
891 if ( isset( $elementor->files_manager ) && method_exists( $elementor->files_manager, 'clear_cache' ) ) {
892 $elementor->files_manager->clear_cache();
893 }
894
895 if ( method_exists( $elementor, 'clear_cache' ) ) {
896 $elementor->clear_cache();
897 }
898 }
899
900 $final_current_version = $manager->get_current_version();
901 $final_new_version = $manager->get_new_version();
902
903 return rest_ensure_response( [
904 'status' => 'success',
905 'message' => $plugin_name . ' database updated successfully.',
906 'previous_version' => $current_version,
907 'new_version' => $final_current_version,
908 'plugin_version' => $final_new_version,
909 'callbacks_executed' => count( $callbacks ),
910 ] );
911 } catch ( \Exception $e ) {
912 return new \WP_Error(
913 'elementor_db_update_failed',
914 'Failed to update ' . $plugin_name . ' database: ' . $e->getMessage(),
915 [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ]
916 );
917 }
918 }
919
920 public function create_temp_backup( $request ) {
921 $plugin_slug = $request->get_param( 'slug' );
922
923 if ( empty( $plugin_slug ) ) {
924 return new \WP_Error( 'missing_slug', 'Plugin slug is required.', [ 'status' => \WP_Http::BAD_REQUEST ] );
925 }
926
927 $backup_result = Temp_Backup::create_plugin_backup( $plugin_slug );
928
929 if ( is_wp_error( $backup_result ) ) {
930 return $backup_result;
931 }
932
933 return rest_ensure_response( [
934 'status' => 'success',
935 'message' => 'Plugin temp backup created successfully.',
936 'slug' => $plugin_slug,
937 ] );
938 }
939 }
940