PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.12
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.12
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / codeinwp / themeisle-sdk / src / Modules / Rollback.php

Rollback.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.10.12, at vendor/codeinwp/themeisle-sdk/src/Modules/Rollback.php

416 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The rollback class for ThemeIsle SDK.
4 *
5 * @package ThemeIsleSDK
6 * @subpackage Rollback
7 * @copyright Copyright (c) 2017, Marius Cristea
8 * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
9 * @since 1.0.0
10 */
11
12 namespace ThemeisleSDK\Modules;
13
14 // Exit if accessed directly.
15 use ThemeisleSDK\Common\Abstract_Module;
16 use ThemeisleSDK\Product;
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 /**
23 * Rollback for ThemeIsle SDK.
24 */
25 class Rollback extends Abstract_Module {
26
27 /**
28 * Add js scripts for themes rollback.
29 */
30 public function add_footer() {
31 $screen = get_current_screen();
32 if ( ! isset( $screen->parent_file ) ) {
33 return;
34 }
35 if ( 'themes.php' !== $screen->parent_file ) {
36 return;
37 }
38 if ( ! $this->product->is_theme() ) {
39 return;
40 }
41 $version = $this->get_rollback();
42 if ( empty( $version ) ) {
43 return;
44 }
45 ?>
46 <script type="text/javascript">
47 jQuery(document).ready(function ($) {
48 setInterval(checkTheme, 500);
49
50 function checkTheme() {
51 var theme = '<?php echo esc_attr( $this->product->get_slug() ); ?>-action';
52
53 if (jQuery('#' + theme).length > 0) {
54 if (jQuery('.theme-overlay.active').is(':visible')) {
55 if (jQuery('#' + theme + '-rollback').length === 0) {
56 jQuery('.theme-actions .active-theme').prepend('<a class="button" style="float:left" id="' + theme + '-rollback" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=' . $this->product->get_key() . '_rollback' ), $this->product->get_key() . '_rollback' ) ); ?>">Rollback to v<?php echo esc_attr( $version['version'] ); ?></a>')
57 }
58 }
59
60 }
61 }
62 })
63
64 </script>
65 <?php
66
67 }
68
69 /**
70 * Get the last rollback for this product.
71 *
72 * @return array The rollback version.
73 */
74 public function get_rollback() {
75 $rollback = array();
76 $versions = $this->get_api_versions();
77 $versions = apply_filters( $this->product->get_key() . '_rollbacks', $versions );
78 if ( empty( $versions ) ) {
79 return $rollback;
80 }
81 if ( $versions ) {
82 usort( $versions, array( $this, 'sort_rollback_array' ) );
83 foreach ( $versions as $version ) {
84 if ( isset( $version['version'] ) && isset( $version['url'] ) && version_compare( $this->product->get_version(), $version['version'], '>' ) ) {
85 $rollback = $version;
86 break;
87 }
88 }
89 }
90
91 return $rollback;
92 }
93
94 /**
95 * Get versions array from wp.org
96 *
97 * @return array Array of versions.
98 */
99 private function get_api_versions() {
100
101 $cache_key = $this->product->get_cache_key();
102 $cache_versions = get_transient( $cache_key );
103 if ( false === $cache_versions ) {
104 $versions = $this->get_remote_versions();
105 set_transient( $cache_key, $versions, 5 * DAY_IN_SECONDS );
106 } else {
107 $versions = is_array( $cache_versions ) ? $cache_versions : array();
108 }
109
110 return $versions;
111 }
112
113 /**
114 * Get remote versions zips.
115 *
116 * @return array Array of available versions.
117 */
118 private function get_remote_versions() {
119 $url = $this->get_versions_api_url();
120 if ( empty( $url ) ) {
121 return [];
122 }
123 $response = function_exists( 'wp_remote_get_wp_remote_get' )
124 ? wp_remote_get_wp_remote_get( $url )
125 : wp_remote_get( $url ); //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
126 if ( is_wp_error( $response ) ) {
127 return array();
128 }
129 $response = wp_remote_retrieve_body( $response );
130
131 if ( is_serialized( $response ) ) {
132 $response = maybe_unserialize( $response );
133 } else {
134 $response = json_decode( $response );
135 }
136
137 if ( ! is_object( $response ) ) {
138 return array();
139 }
140 if ( ! isset( $response->versions ) ) {
141 return array();
142 }
143
144 $versions = array();
145 foreach ( $response->versions as $key => $value ) {
146 $versions[] = array(
147 'version' => is_object( $value ) ? $value->version : $key,
148 'url' => is_object( $value ) ? $value->file : $value,
149 );
150 }
151
152 return $versions;
153 }
154
155 /**
156 * Return url where to check for versions.
157 *
158 * @return string Url where to check for versions.
159 */
160 private function get_versions_api_url() {
161 if ( $this->product->is_wordpress_available() && $this->product->is_plugin() ) {
162 return sprintf( 'https://api.wordpress.org/plugins/info/1.0/%s', $this->product->get_slug() );
163 }
164 if ( $this->product->is_wordpress_available() && $this->product->is_theme() ) {
165 return sprintf( 'https://api.wordpress.org/themes/info/1.1/?action=theme_information&request[slug]=%s&request[fields][versions]=true', $this->product->get_slug() );
166 }
167 $license = $this->product->get_license();
168 if ( $this->product->requires_license() && strlen( $license ) < 10 ) {
169 return '';
170 }
171
172 return sprintf( '%slicense/versions/%s/%s/%s/%s', Product::API_URL, rawurlencode( $this->product->get_name() ), $license, urlencode( get_site_url() ), $this->product->get_version() );
173 }
174
175 /**
176 * Show the rollback links in the plugin page.
177 *
178 * @param array $links Plugin links.
179 *
180 * @return array $links Altered links.
181 */
182 public function add_rollback_link( $links ) {
183 $version = $this->get_rollback();
184 if ( empty( $version ) ) {
185 return $links;
186 }
187 $links[] = '<a href="' . wp_nonce_url( admin_url( 'admin-post.php?action=' . $this->product->get_key() . '_rollback' ), $this->product->get_key() . '_rollback' ) . '">' . sprintf( apply_filters( $this->product->get_key() . '_rollback_label', 'Rollback to v%s' ), $version['version'] ) . '</a>';
188
189 return $links;
190 }
191
192 /**
193 * Start the rollback operation.
194 */
195 public function start_rollback() {
196 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], $this->product->get_key() . '_rollback' ) ) { //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
197 wp_nonce_ays( '' );
198 }
199
200 if ( $this->product->is_plugin() ) {
201 $this->start_rollback_plugin();
202
203 return;
204 }
205 if ( $this->product->is_theme() ) {
206 $this->start_rollback_theme();
207
208 return;
209 }
210 }
211
212 /**
213 * Start the rollback operation for the plugin.
214 */
215 private function start_rollback_plugin() {
216 $rollback = $this->get_rollback();
217 $plugin_transient = get_site_transient( 'update_plugins' );
218 $plugin_folder = $this->product->get_slug();
219 $plugin_file = $this->product->get_file();
220 $version = $rollback['version'];
221 $temp_array = array(
222 'slug' => $plugin_folder,
223 'new_version' => $version,
224 'package' => $rollback['url'],
225 );
226
227 $temp_object = (object) $temp_array;
228 $plugin_transient->response[ $plugin_folder . '/' . $plugin_file ] = $temp_object;
229 set_site_transient( 'update_plugins', $plugin_transient );
230
231 $transient = get_transient( $this->product->get_key() . '_warning_rollback' );
232
233 // Style fix for the api link that gets outside the content.
234 echo '<style>body#error-page{word-break:break-word;}</style>';
235
236 if ( false === $transient ) {
237 set_transient( $this->product->get_key() . '_warning_rollback', 'in progress', 30 );
238 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
239 $title = sprintf( apply_filters( $this->product->get_key() . '_rollback_message', 'Rolling back %s to v%s' ), $this->product->get_name(), $version );
240 $plugin = $plugin_folder . '/' . $plugin_file;
241 $nonce = 'upgrade-plugin_' . $plugin;
242 $url = 'update.php?action=upgrade-plugin&plugin=' . urlencode( $plugin );
243 $upgrader_skin = new \Plugin_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'plugin' ) );
244 $upgrader = new \Plugin_Upgrader( $upgrader_skin );
245 $upgrader->upgrade( $plugin );
246 delete_transient( $this->product->get_key() . '_warning_rollback' );
247 wp_die(
248 '',
249 esc_attr( $title ),
250 array(
251 'response' => 200,
252 )
253 );
254 }
255 }
256
257 /**
258 * Start the rollback operation for the theme.
259 */
260 private function start_rollback_theme() {
261 add_filter( 'update_theme_complete_actions', array( $this, 'alter_links_theme_upgrade' ) );
262 $rollback = $this->get_rollback();
263 $transient = get_site_transient( 'update_themes' );
264 $folder = $this->product->get_slug();
265 $version = $rollback['version'];
266 $temp_array = array(
267 'new_version' => $version,
268 'package' => $rollback['url'],
269 );
270
271 $transient->response[ $folder . '/style.css' ] = $temp_array;
272 set_site_transient( 'update_themes', $transient );
273
274 $transient = get_transient( $this->product->get_key() . '_warning_rollback' );
275
276 // Style fix for the api link that gets outside the content.
277 echo '<style>body#error-page{word-break:break-word;}</style>';
278
279 if ( false === $transient ) {
280 set_transient( $this->product->get_key() . '_warning_rollback', 'in progress', 30 );
281 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
282 $title = sprintf( apply_filters( $this->product->get_key() . '_rollback_message', 'Rolling back %s to v%s' ), $this->product->get_name(), $version );
283 $theme = $folder . '/style.css';
284 $nonce = 'upgrade-theme_' . $theme;
285 $url = 'update.php?action=upgrade-theme&theme=' . urlencode( $theme );
286
287 /**
288 * The rollback will attach a temporary theme for the rollback to the transient.
289 * However, when executing the upgrade for the attached theme we need to change the slug to the original theme slug.
290 * This is because it will use the slug to create a temp folder for the theme used during the upgrade.
291 */
292 add_filter(
293 'upgrader_package_options',
294 function( $options ) use ( $folder, $theme ) {
295 if ( isset( $options['hook_extra']['theme'] ) && $options['hook_extra']['theme'] === $theme && isset( $options['hook_extra']['temp_backup']['slug'] ) ) {
296 $options['hook_extra']['temp_backup']['slug'] = $folder;
297 }
298 return $options;
299 }
300 );
301
302 $upgrader = new \Theme_Upgrader( new \Theme_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'theme' ) ) );
303 $upgrader->upgrade( $theme );
304 delete_transient( $this->product->get_key() . '_warning_rollback' );
305 wp_die(
306 '',
307 esc_attr( $title ),
308 array(
309 'response' => 200,
310 )
311 );
312 }
313 }
314
315 /**
316 * Alter links and remove duplicate customize message.
317 *
318 * @param array $links Array of old links.
319 *
320 * @return mixed Array of links.
321 */
322 public function alter_links_theme_upgrade( $links ) {
323 if ( isset( $links['preview'] ) ) {
324 $links['preview'] = str_replace( '<span aria-hidden="true">Customize</span>', '', $links['preview'] );
325 }
326
327 return $links;
328 }
329
330 /**
331 * Loads product object.
332 *
333 * @param Product $product Product object.
334 *
335 * @return bool Should we load the module?
336 */
337 public function can_load( $product ) {
338 if ( $this->is_from_partner( $product ) ) {
339 return false;
340 }
341 if ( $product->is_theme() && ! current_user_can( 'switch_themes' ) ) {
342 return false;
343 }
344
345 if ( $product->is_plugin() && ! current_user_can( 'install_plugins' ) ) {
346 return false;
347 }
348
349 return true;
350 }
351
352 /**
353 * Sort the rollbacks array in descending order.
354 *
355 * @param mixed $a First version to compare.
356 * @param mixed $b Second version to compare.
357 *
358 * @return bool Which version is greater?
359 */
360 public function sort_rollback_array( $a, $b ) {
361 return version_compare( $b['version'], $a['version'] );
362 }
363
364 /**
365 * Load module logic.
366 *
367 * @param Product $product Product object.
368 *
369 * @return $this Module object.
370 */
371 public function load( $product ) {
372 $this->product = $product;
373 $this->show_link();
374 $this->add_hooks();
375
376 return $this;
377 }
378
379 /**
380 * If product can be rolled back, show the link to rollback.
381 */
382 private function show_link() {
383 add_filter(
384 'plugin_action_links_' . plugin_basename( $this->product->get_basefile() ),
385 array(
386 $this,
387 'add_rollback_link',
388 )
389 );
390 }
391
392 /**
393 * Fires after the option has been updated.
394 *
395 * @param mixed $old_value The old option value.
396 * @param mixed $value The new option value.
397 * @param string $option Option name.
398 */
399 public function update_active_plugins_action( $old_value, $value, $option ) {
400 delete_site_transient( 'update_plugins' );
401 wp_cache_delete( 'plugins', 'plugins' );
402 }
403
404 /**
405 * Set the rollback hook. Strangely, this does not work if placed in the ThemeIsle_SDK_Rollback class, so it is being called from there instead.
406 */
407 public function add_hooks() {
408 add_action( 'admin_post_' . $this->product->get_key() . '_rollback', array( $this, 'start_rollback' ) );
409 add_action( 'admin_footer', array( $this, 'add_footer' ) );
410
411 // This hook will be invoked after the plugin activation.
412 // We use this to force an update of the cache so that Update is present immediate after a rollback.
413 add_action( 'update_option_active_plugins', array( $this, 'update_active_plugins_action' ), 10, 3 );
414 }
415 }
416