PluginProbe ʕ •ᴥ•ʔ
Starter Sites & Templates by Neve / 1.4.1
Starter Sites & Templates by Neve v1.4.1
1.4.1 1.4.0 1.3.0 1.2.29 1.2.28 1.2.6 1.2.7 1.2.8 1.2.9 trunk 1.0.10 1.0.11 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.27 1.1.28 1.1.29 1.1.3 1.1.30 1.1.31 1.1.32 1.1.33 1.1.34 1.1.35 1.1.36 1.1.37 1.1.38 1.1.39 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.3 1.2.4 1.2.5
templates-patterns-collection / vendor / codeinwp / themeisle-sdk / src / Modules / Rollback.php
templates-patterns-collection / vendor / codeinwp / themeisle-sdk / src / Modules Last commit date
About_us.php 3 weeks ago Abstract_Migration.php 2 months ago Announcements.php 2 months ago Compatibilities.php 2 years ago Dashboard_widget.php 3 weeks ago Featured_plugins.php 1 month ago Float_widget.php 1 year ago Licenser.php 2 months ago Logger.php 10 months ago Migrator.php 2 months ago Notification.php 3 years ago Promotions.php 3 weeks ago Recommendation.php 3 years ago Review.php 1 year ago Rollback.php 1 year ago Script_loader.php 1 year ago Translate.php 5 years ago Translations.php 1 year ago Uninstall_feedback.php 2 days ago Welcome.php 2 years ago
Rollback.php
418 lines
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\Loader;
17 use ThemeisleSDK\Product;
18
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit;
21 }
22
23 /**
24 * Rollback for ThemeIsle SDK.
25 */
26 class Rollback extends Abstract_Module {
27
28 /**
29 * Add js scripts for themes rollback.
30 */
31 public function add_footer() {
32 $screen = get_current_screen();
33 if ( ! isset( $screen->parent_file ) ) {
34 return;
35 }
36 if ( 'themes.php' !== $screen->parent_file ) {
37 return;
38 }
39 if ( ! $this->product->is_theme() ) {
40 return;
41 }
42 $version = $this->get_rollback();
43 if ( empty( $version ) ) {
44 return;
45 }
46 ?>
47 <script type="text/javascript">
48 jQuery(document).ready(function ($) {
49 setInterval(checkTheme, 500);
50
51 function checkTheme() {
52 var theme = '<?php echo esc_attr( $this->product->get_slug() ); ?>-action';
53
54 if (jQuery('#' + theme).length > 0) {
55 if (jQuery('.theme-overlay.active').is(':visible')) {
56 if (jQuery('#' + theme + '-rollback').length === 0) {
57 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>')
58 }
59 }
60
61 }
62 }
63 })
64
65 </script>
66 <?php
67
68 }
69
70 /**
71 * Get the last rollback for this product.
72 *
73 * @return array The rollback version.
74 */
75 public function get_rollback() {
76 $rollback = array();
77 $versions = $this->get_api_versions();
78 $versions = apply_filters( $this->product->get_key() . '_rollbacks', $versions );
79 if ( empty( $versions ) ) {
80 return $rollback;
81 }
82 if ( $versions ) {
83 usort( $versions, array( $this, 'sort_rollback_array' ) );
84 foreach ( $versions as $version ) {
85 if ( isset( $version['version'] ) && isset( $version['url'] ) && version_compare( $this->product->get_version(), $version['version'], '>' ) ) {
86 $rollback = $version;
87 break;
88 }
89 }
90 }
91
92 return $rollback;
93 }
94
95 /**
96 * Get versions array from wp.org
97 *
98 * @return array Array of versions.
99 */
100 private function get_api_versions() {
101
102 $cache_key = $this->product->get_cache_key();
103 $cache_versions = get_transient( $cache_key );
104 if ( false === $cache_versions ) {
105 $versions = $this->get_remote_versions();
106 set_transient( $cache_key, $versions, 5 * DAY_IN_SECONDS );
107 } else {
108 $versions = is_array( $cache_versions ) ? $cache_versions : array();
109 }
110
111 return $versions;
112 }
113
114 /**
115 * Get remote versions zips.
116 *
117 * @return array Array of available versions.
118 */
119 private function get_remote_versions() {
120 $url = $this->get_versions_api_url();
121 if ( empty( $url ) ) {
122 return [];
123 }
124 $response = function_exists( 'vip_safe_wp_remote_get' )
125 ? vip_safe_wp_remote_get( $url )
126 : wp_remote_get( $url ); //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
127 if ( is_wp_error( $response ) ) {
128 return array();
129 }
130 $response = wp_remote_retrieve_body( $response );
131
132 if ( is_serialized( $response ) ) {
133 $response = maybe_unserialize( $response );
134 } else {
135 $response = json_decode( $response );
136 }
137
138 if ( ! is_object( $response ) ) {
139 return array();
140 }
141 if ( ! isset( $response->versions ) ) {
142 return array();
143 }
144
145 $versions = array();
146 foreach ( $response->versions as $key => $value ) {
147 $versions[] = array(
148 'version' => is_object( $value ) ? $value->version : $key,
149 'url' => is_object( $value ) ? $value->file : $value,
150 );
151 }
152
153 return $versions;
154 }
155
156 /**
157 * Return url where to check for versions.
158 *
159 * @return string Url where to check for versions.
160 */
161 private function get_versions_api_url() {
162 if ( $this->product->is_wordpress_available() && $this->product->is_plugin() ) {
163 return sprintf( 'https://api.wordpress.org/plugins/info/1.0/%s', $this->product->get_slug() );
164 }
165 if ( $this->product->is_wordpress_available() && $this->product->is_theme() ) {
166 return sprintf( 'https://api.wordpress.org/themes/info/1.1/?action=theme_information&request[slug]=%s&request[fields][versions]=true', $this->product->get_slug() );
167 }
168 $license = $this->product->get_license();
169 if ( $this->product->requires_license() && strlen( $license ) < 10 ) {
170 return '';
171 }
172
173 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() );
174 }
175
176 /**
177 * Show the rollback links in the plugin page.
178 *
179 * @param array $links Plugin links.
180 *
181 * @return array $links Altered links.
182 */
183 public function add_rollback_link( $links ) {
184 $version = $this->get_rollback();
185 if ( empty( $version ) ) {
186 return $links;
187 }
188 $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', Loader::$labels['rollback']['cta'] ), $version['version'] ) . '</a>';
189
190 return $links;
191 }
192
193 /**
194 * Start the rollback operation.
195 */
196 public function start_rollback() {
197 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], $this->product->get_key() . '_rollback' ) ) { //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
198 wp_nonce_ays( '' );
199 }
200
201 if ( $this->product->is_plugin() ) {
202 $this->start_rollback_plugin();
203
204 return;
205 }
206 if ( $this->product->is_theme() ) {
207 $this->start_rollback_theme();
208
209 return;
210 }
211 }
212
213 /**
214 * Start the rollback operation for the plugin.
215 */
216 private function start_rollback_plugin() {
217 $rollback = $this->get_rollback();
218 $plugin_transient = get_site_transient( 'update_plugins' );
219 $plugin_folder = $this->product->get_slug();
220 $plugin_file = $this->product->get_file();
221 $version = $rollback['version'];
222 $temp_array = array(
223 'slug' => $plugin_folder,
224 'new_version' => $version,
225 'package' => $rollback['url'],
226 );
227
228 $temp_object = (object) $temp_array;
229 $plugin_transient->response[ $plugin_folder . '/' . $plugin_file ] = $temp_object;
230 set_site_transient( 'update_plugins', $plugin_transient );
231
232 $transient = get_transient( $this->product->get_key() . '_warning_rollback' );
233
234 // Style fix for the api link that gets outside the content.
235 echo '<style>body#error-page{word-break:break-word;}</style>';
236
237 if ( false === $transient ) {
238 set_transient( $this->product->get_key() . '_warning_rollback', 'in progress', 30 );
239 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
240 $title = sprintf( apply_filters( $this->product->get_key() . '_rollback_message', 'Rolling back %s to v%s' ), $this->product->get_name(), $version );
241 $plugin = $plugin_folder . '/' . $plugin_file;
242 $nonce = 'upgrade-plugin_' . $plugin;
243 $url = 'update.php?action=upgrade-plugin&plugin=' . urlencode( $plugin );
244 $upgrader_skin = new \Plugin_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'plugin' ) );
245 $upgrader = new \Plugin_Upgrader( $upgrader_skin );
246 $upgrader->upgrade( $plugin );
247 delete_transient( $this->product->get_key() . '_warning_rollback' );
248 wp_die(
249 '',
250 esc_attr( $title ),
251 array(
252 'response' => 200,
253 )
254 );
255 }
256 }
257
258 /**
259 * Start the rollback operation for the theme.
260 */
261 private function start_rollback_theme() {
262 add_filter( 'update_theme_complete_actions', array( $this, 'alter_links_theme_upgrade' ) );
263 $rollback = $this->get_rollback();
264 $transient = get_site_transient( 'update_themes' );
265 $folder = $this->product->get_slug();
266 $version = $rollback['version'];
267 $temp_array = array(
268 'new_version' => $version,
269 'package' => $rollback['url'],
270 );
271
272 $transient->response[ $folder . '/style.css' ] = $temp_array;
273 set_site_transient( 'update_themes', $transient );
274
275 $transient = get_transient( $this->product->get_key() . '_warning_rollback' );
276
277 // Style fix for the api link that gets outside the content.
278 echo '<style>body#error-page{word-break:break-word;}</style>';
279
280 if ( false === $transient ) {
281 set_transient( $this->product->get_key() . '_warning_rollback', 'in progress', 30 );
282 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
283 $title = sprintf( apply_filters( $this->product->get_key() . '_rollback_message', 'Rolling back %s to v%s' ), $this->product->get_name(), $version );
284 $theme = $folder . '/style.css';
285 $nonce = 'upgrade-theme_' . $theme;
286 $url = 'update.php?action=upgrade-theme&theme=' . urlencode( $theme );
287
288 /**
289 * The rollback will attach a temporary theme for the rollback to the transient.
290 * However, when executing the upgrade for the attached theme we need to change the slug to the original theme slug.
291 * This is because it will use the slug to create a temp folder for the theme used during the upgrade.
292 */
293 add_filter(
294 'upgrader_package_options',
295 function ( $options ) use ( $folder, $theme ) {
296 if ( isset( $options['hook_extra']['theme'] ) && $options['hook_extra']['theme'] === $theme && isset( $options['hook_extra']['temp_backup']['slug'] ) ) {
297 $options['hook_extra']['temp_backup']['slug'] = $folder;
298 }
299
300 return $options;
301 }
302 );
303
304 $upgrader = new \Theme_Upgrader( new \Theme_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'theme' ) ) );
305 $upgrader->upgrade( $theme );
306 delete_transient( $this->product->get_key() . '_warning_rollback' );
307 wp_die(
308 '',
309 esc_attr( $title ),
310 array(
311 'response' => 200,
312 )
313 );
314 }
315 }
316
317 /**
318 * Alter links and remove duplicate customize message.
319 *
320 * @param array $links Array of old links.
321 *
322 * @return mixed Array of links.
323 */
324 public function alter_links_theme_upgrade( $links ) {
325 if ( isset( $links['preview'] ) ) {
326 $links['preview'] = str_replace( '<span aria-hidden="true">Customize</span>', '', $links['preview'] );
327 }
328
329 return $links;
330 }
331
332 /**
333 * Loads product object.
334 *
335 * @param Product $product Product object.
336 *
337 * @return bool Should we load the module?
338 */
339 public function can_load( $product ) {
340 if ( $this->is_from_partner( $product ) ) {
341 return false;
342 }
343 if ( $product->is_theme() && ! current_user_can( 'switch_themes' ) ) {
344 return false;
345 }
346
347 if ( $product->is_plugin() && ! current_user_can( 'install_plugins' ) ) {
348 return false;
349 }
350
351 return true;
352 }
353
354 /**
355 * Sort the rollbacks array in descending order.
356 *
357 * @param mixed $a First version to compare.
358 * @param mixed $b Second version to compare.
359 *
360 * @return bool Which version is greater?
361 */
362 public function sort_rollback_array( $a, $b ) {
363 return version_compare( $b['version'], $a['version'] );
364 }
365
366 /**
367 * Load module logic.
368 *
369 * @param Product $product Product object.
370 *
371 * @return $this Module object.
372 */
373 public function load( $product ) {
374 $this->product = $product;
375 $this->show_link();
376 $this->add_hooks();
377
378 return $this;
379 }
380
381 /**
382 * If product can be rolled back, show the link to rollback.
383 */
384 private function show_link() {
385 add_filter(
386 'plugin_action_links_' . plugin_basename( $this->product->get_basefile() ),
387 array(
388 $this,
389 'add_rollback_link',
390 )
391 );
392 }
393
394 /**
395 * Fires after the option has been updated.
396 *
397 * @param mixed $old_value The old option value.
398 * @param mixed $value The new option value.
399 * @param string $option Option name.
400 */
401 public function update_active_plugins_action( $old_value, $value, $option ) {
402 delete_site_transient( 'update_plugins' );
403 wp_cache_delete( 'plugins', 'plugins' );
404 }
405
406 /**
407 * 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.
408 */
409 public function add_hooks() {
410 add_action( 'admin_post_' . $this->product->get_key() . '_rollback', array( $this, 'start_rollback' ) );
411 add_action( 'admin_footer', array( $this, 'add_footer' ) );
412
413 // This hook will be invoked after the plugin activation.
414 // We use this to force an update of the cache so that Update is present immediate after a rollback.
415 add_action( 'update_option_active_plugins', array( $this, 'update_active_plugins_action' ), 10, 3 );
416 }
417 }
418