2020-04-07 13:03:04 +00:00
< ? php
/**
* The rollback class for ThemeIsle SDK .
*
* @ package ThemeIsleSDK
* @ subpackage Rollback
* @ copyright Copyright ( c ) 2017 , Marius Cristea
* @ license http :// opensource . org / licenses / gpl - 3.0 . php GNU Public License
* @ since 1.0 . 0
*/
namespace ThemeisleSDK\Modules ;
// Exit if accessed directly.
use ThemeisleSDK\Common\Abstract_Module ;
use ThemeisleSDK\Product ;
if ( ! defined ( 'ABSPATH' ) ) {
exit ;
}
/**
* Rollback for ThemeIsle SDK .
*/
class Rollback extends Abstract_Module {
/**
* Add js scripts for themes rollback .
*/
public function add_footer () {
$screen = get_current_screen ();
if ( ! isset ( $screen -> parent_file ) ) {
return ;
}
if ( 'themes.php' !== $screen -> parent_file ) {
return ;
}
if ( ! $this -> product -> is_theme () ) {
return ;
}
$version = $this -> get_rollback ();
if ( empty ( $version ) ) {
return ;
}
?>
< script type = " text/javascript " >
jQuery ( document ) . ready ( function ( $ ) {
setInterval ( checkTheme , 500 );
function checkTheme () {
var theme = '<?php echo esc_attr( $this->product->get_slug() ); ?>-action' ;
if ( jQuery ( '#' + theme ) . length > 0 ) {
if ( jQuery ( '.theme-overlay.active' ) . is ( ':visible' )) {
if ( jQuery ( '#' + theme + '-rollback' ) . length === 0 ) {
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>' )
}
}
}
}
})
</ script >
< ? php
}
/**
* Get the last rollback for this product .
*
* @ return array The rollback version .
*/
public function get_rollback () {
$rollback = array ();
$versions = $this -> get_api_versions ();
$versions = apply_filters ( $this -> product -> get_key () . '_rollbacks' , $versions );
if ( empty ( $versions ) ) {
return $rollback ;
}
if ( $versions ) {
usort ( $versions , array ( $this , 'sort_rollback_array' ) );
foreach ( $versions as $version ) {
if ( isset ( $version [ 'version' ] ) && isset ( $version [ 'url' ] ) && version_compare ( $this -> product -> get_version (), $version [ 'version' ], '>' ) ) {
$rollback = $version ;
break ;
}
}
}
return $rollback ;
}
/**
* Get versions array from wp . org
*
* @ return array Array of versions .
*/
private function get_api_versions () {
2022-09-02 15:19:58 +00:00
$cache_key = $this -> product -> get_cache_key ();
2020-04-07 13:03:04 +00:00
$cache_versions = get_transient ( $cache_key );
if ( false === $cache_versions ) {
$versions = $this -> get_remote_versions ();
set_transient ( $cache_key , $versions , 5 * DAY_IN_SECONDS );
} else {
$versions = is_array ( $cache_versions ) ? $cache_versions : array ();
}
return $versions ;
}
/**
* Get remote versions zips .
*
* @ return array Array of available versions .
*/
private function get_remote_versions () {
$url = $this -> get_versions_api_url ();
if ( empty ( $url ) ) {
return [];
}
2021-07-25 23:25:06 +00:00
$response = function_exists ( 'wp_remote_get_wp_remote_get' )
? wp_remote_get_wp_remote_get ( $url )
: wp_remote_get ( $url ); //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
2020-04-07 13:03:04 +00:00
if ( is_wp_error ( $response ) ) {
return array ();
}
$response = wp_remote_retrieve_body ( $response );
if ( is_serialized ( $response ) ) {
$response = maybe_unserialize ( $response );
} else {
$response = json_decode ( $response );
}
if ( ! is_object ( $response ) ) {
return array ();
}
if ( ! isset ( $response -> versions ) ) {
return array ();
}
$versions = array ();
foreach ( $response -> versions as $key => $value ) {
$versions [] = array (
'version' => is_object ( $value ) ? $value -> version : $key ,
'url' => is_object ( $value ) ? $value -> file : $value ,
);
}
return $versions ;
}
/**
* Return url where to check for versions .
*
* @ return string Url where to check for versions .
*/
private function get_versions_api_url () {
if ( $this -> product -> is_wordpress_available () && $this -> product -> is_plugin () ) {
return sprintf ( 'https://api.wordpress.org/plugins/info/1.0/%s' , $this -> product -> get_slug () );
}
if ( $this -> product -> is_wordpress_available () && $this -> product -> is_theme () ) {
return sprintf ( 'https://api.wordpress.org/themes/info/1.1/?action=theme_information&request[slug]=%s&request[fields][versions]=true' , $this -> product -> get_slug () );
}
$license = $this -> product -> get_license ();
if ( $this -> product -> requires_license () && strlen ( $license ) < 10 ) {
return '' ;
}
2020-07-24 14:08:54 +00:00
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 () );
2020-04-07 13:03:04 +00:00
}
/**
* Show the rollback links in the plugin page .
*
* @ param array $links Plugin links .
*
* @ return array $links Altered links .
*/
public function add_rollback_link ( $links ) {
$version = $this -> get_rollback ();
if ( empty ( $version ) ) {
return $links ;
}
$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>' ;
return $links ;
}
/**
* Start the rollback operation .
*/
public function start_rollback () {
2021-07-25 23:25:06 +00:00
if ( ! isset ( $_GET [ '_wpnonce' ] ) || ! wp_verify_nonce ( $_GET [ '_wpnonce' ], $this -> product -> get_key () . '_rollback' ) ) { //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2020-04-07 13:03:04 +00:00
wp_nonce_ays ( '' );
}
if ( $this -> product -> is_plugin () ) {
$this -> start_rollback_plugin ();
return ;
}
if ( $this -> product -> is_theme () ) {
$this -> start_rollback_theme ();
return ;
}
}
/**
* Start the rollback operation for the plugin .
*/
private function start_rollback_plugin () {
$rollback = $this -> get_rollback ();
$plugin_transient = get_site_transient ( 'update_plugins' );
$plugin_folder = $this -> product -> get_slug ();
$plugin_file = $this -> product -> get_file ();
$version = $rollback [ 'version' ];
$temp_array = array (
'slug' => $plugin_folder ,
'new_version' => $version ,
'package' => $rollback [ 'url' ],
);
$temp_object = ( object ) $temp_array ;
$plugin_transient -> response [ $plugin_folder . '/' . $plugin_file ] = $temp_object ;
set_site_transient ( 'update_plugins' , $plugin_transient );
$transient = get_transient ( $this -> product -> get_key () . '_warning_rollback' );
2021-07-25 23:25:06 +00:00
// Style fix for the api link that gets outside the content.
echo '<style>body#error-page{word-break:break-word;}</style>' ;
2020-04-07 13:03:04 +00:00
if ( false === $transient ) {
set_transient ( $this -> product -> get_key () . '_warning_rollback' , 'in progress' , 30 );
2021-07-25 23:25:06 +00:00
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ;
2020-04-07 13:03:04 +00:00
$title = sprintf ( apply_filters ( $this -> product -> get_key () . '_rollback_message' , 'Rolling back %s to v%s' ), $this -> product -> get_name (), $version );
$plugin = $plugin_folder . '/' . $plugin_file ;
$nonce = 'upgrade-plugin_' . $plugin ;
$url = 'update.php?action=upgrade-plugin&plugin=' . urlencode ( $plugin );
$upgrader_skin = new \Plugin_Upgrader_Skin ( compact ( 'title' , 'nonce' , 'url' , 'plugin' ) );
$upgrader = new \Plugin_Upgrader ( $upgrader_skin );
$upgrader -> upgrade ( $plugin );
delete_transient ( $this -> product -> get_key () . '_warning_rollback' );
wp_die (
'' ,
2021-07-25 23:25:06 +00:00
esc_attr ( $title ),
2020-04-07 13:03:04 +00:00
array (
'response' => 200 ,
)
);
}
}
/**
* Start the rollback operation for the theme .
*/
private function start_rollback_theme () {
add_filter ( 'update_theme_complete_actions' , array ( $this , 'alter_links_theme_upgrade' ) );
$rollback = $this -> get_rollback ();
$transient = get_site_transient ( 'update_themes' );
$folder = $this -> product -> get_slug ();
$version = $rollback [ 'version' ];
$temp_array = array (
'new_version' => $version ,
'package' => $rollback [ 'url' ],
);
$transient -> response [ $folder . '/style.css' ] = $temp_array ;
set_site_transient ( 'update_themes' , $transient );
$transient = get_transient ( $this -> product -> get_key () . '_warning_rollback' );
2021-07-25 23:25:06 +00:00
// Style fix for the api link that gets outside the content.
echo '<style>body#error-page{word-break:break-word;}</style>' ;
2020-04-07 13:03:04 +00:00
if ( false === $transient ) {
set_transient ( $this -> product -> get_key () . '_warning_rollback' , 'in progress' , 30 );
2021-07-25 23:25:06 +00:00
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ;
2020-04-07 13:03:04 +00:00
$title = sprintf ( apply_filters ( $this -> product -> get_key () . '_rollback_message' , 'Rolling back %s to v%s' ), $this -> product -> get_name (), $version );
$theme = $folder . '/style.css' ;
$nonce = 'upgrade-theme_' . $theme ;
$url = 'update.php?action=upgrade-theme&theme=' . urlencode ( $theme );
$upgrader = new \Theme_Upgrader ( new \Theme_Upgrader_Skin ( compact ( 'title' , 'nonce' , 'url' , 'theme' ) ) );
$upgrader -> upgrade ( $theme );
delete_transient ( $this -> product -> get_key () . '_warning_rollback' );
wp_die (
'' ,
2021-07-25 23:25:06 +00:00
esc_attr ( $title ),
2020-04-07 13:03:04 +00:00
array (
'response' => 200 ,
)
);
}
}
/**
* Alter links and remove duplicate customize message .
*
* @ param array $links Array of old links .
*
* @ return mixed Array of links .
*/
public function alter_links_theme_upgrade ( $links ) {
if ( isset ( $links [ 'preview' ] ) ) {
$links [ 'preview' ] = str_replace ( '<span aria-hidden="true">Customize</span>' , '' , $links [ 'preview' ] );
}
return $links ;
}
/**
* Loads product object .
*
* @ param Product $product Product object .
*
* @ return bool Should we load the module ?
*/
public function can_load ( $product ) {
if ( $this -> is_from_partner ( $product ) ) {
return false ;
}
if ( $product -> is_theme () && ! current_user_can ( 'switch_themes' ) ) {
return false ;
}
if ( $product -> is_plugin () && ! current_user_can ( 'install_plugins' ) ) {
return false ;
}
return true ;
}
/**
* Sort the rollbacks array in descending order .
*
* @ param mixed $a First version to compare .
* @ param mixed $b Second version to compare .
*
* @ return bool Which version is greater ?
*/
public function sort_rollback_array ( $a , $b ) {
2021-07-25 23:25:06 +00:00
return version_compare ( $b [ 'version' ], $a [ 'version' ] );
2020-04-07 13:03:04 +00:00
}
/**
* Load module logic .
*
* @ param Product $product Product object .
*
* @ return $this Module object .
*/
public function load ( $product ) {
$this -> product = $product ;
$this -> show_link ();
$this -> add_hooks ();
return $this ;
}
/**
* If product can be rolled back , show the link to rollback .
*/
private function show_link () {
add_filter (
'plugin_action_links_' . plugin_basename ( $this -> product -> get_basefile () ),
array (
$this ,
'add_rollback_link' ,
)
);
}
2022-09-02 15:19:58 +00:00
/**
* Fires after the option has been updated .
*
* @ param mixed $old_value The old option value .
* @ param mixed $value The new option value .
* @ param string $option Option name .
*/
public function update_active_plugins_action ( $old_value , $value , $option ) {
delete_site_transient ( 'update_plugins' );
wp_cache_delete ( 'plugins' , 'plugins' );
}
2020-04-07 13:03:04 +00:00
/**
* 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 .
*/
public function add_hooks () {
add_action ( 'admin_post_' . $this -> product -> get_key () . '_rollback' , array ( $this , 'start_rollback' ) );
add_action ( 'admin_footer' , array ( $this , 'add_footer' ) );
2022-09-02 15:19:58 +00:00
// This hook will be invoked after the plugin activation.
// We use this to force an update of the cache so that Update is present immediate after a rollback.
add_action ( 'update_option_active_plugins' , array ( $this , 'update_active_plugins_action' ), 10 , 3 );
2020-04-07 13:03:04 +00:00
}
}