File: /home/zwafgyp/lacalita/wp-content/plugins/rey-core/inc/fw/out-of-sync.php
<?php
namespace ReyCore;
if (!defined('ABSPATH')) exit; // Exit if accessed directly
/**
* Handles out of sync notices to force major updates on both Core and Theme.
*/
class OutOfSync
{
private static $items = [];
const THEME_SLUG = 'rey';
const CORE_SLUG = 'rey-core';
public function __construct()
{
// determine which lib to load
// and if it should load
if( ! self::should_load() ){
return;
}
define('REY_OUTDATED', true);
// Legacy, remove old hooks
if( class_exists('\ReyTheme_Base') && is_callable(['ReyTheme_Base', 'out_of_sync']) && function_exists('reycore__remove_filters_for_anonymous_class') ){
reycore__remove_filters_for_anonymous_class( 'admin_notices', 'ReyTheme_Base', 'out_of_sync', 10 );
reycore__remove_filters_for_anonymous_class( 'wp_body_open', 'ReyTheme_Base', 'out_of_sync', 10 );
}
add_action( 'admin_notices', [$this, 'render_notice'] );
add_action( 'wp_body_open', [$this, 'render_notice'] );
add_action( 'wp_ajax_rey_outdated_link', [$this, 'ajax__outdated_link'] );
add_action( 'wp_ajax_rey_download_link', [$this, 'ajax__download_link'] );
add_action( 'wp_ajax_rey_get_actions', [$this, 'ajax__get_actions'] );
}
/**
* Determines if the library should load
*
* @return boolean
*/
public static function should_load(){
if( ! (current_user_can('administrator') || current_user_can('install_plugins')) ){
return;
}
self::$items = [
self::THEME_SLUG => [
'version_constant' => 'REY_THEME_VERSION',
'type' => 'theme',
'basename' => self::THEME_SLUG,
],
self::CORE_SLUG => [
'version_constant' => 'REY_CORE_VERSION',
'type' => 'plugin',
'basename' => 'rey-core/rey-core.php',
],
];
$theme_version = self::$items[self::THEME_SLUG]['version_constant'];
$core_version = self::$items[self::CORE_SLUG]['version_constant'];
// must have both Core and Theme active
if( ! ( defined( $theme_version ) && defined( $core_version ) ) ){
return false;
}
self::$items[self::THEME_SLUG]['major_version'] = self::get_item_major_version( constant($theme_version) );
self::$items[self::CORE_SLUG]['major_version'] = self::get_item_major_version( constant($core_version) );
// if both major versions are similar, no need to continue
if( self::$items[self::THEME_SLUG]['major_version'] === self::$items[self::CORE_SLUG]['major_version'] ){
return false;
}
$ns = strtolower(__NAMESPACE__);
// we're inside Core
if( $ns === str_replace('-', '', self::CORE_SLUG) ){
$v1 = constant($core_version);
$v2 = constant($theme_version);
}
// we're inside theme
elseif( $ns === self::THEME_SLUG ) {
$v1 = constant(self::$items[self::THEME_SLUG]['version_constant']);
$v2 = constant(self::$items[self::CORE_SLUG]['version_constant']);
}
return version_compare( $v1, $v2, '>' );
}
/**
* Retrieves the items.
*
* @return array
*/
public static function get_items(){
if( function_exists('rey__get_props') ):
// define only if undefined
if( ! isset(self::$items[self::THEME_SLUG]['name']) ){
self::$items[self::THEME_SLUG]['name'] = rey__get_props('theme_title');
}
if( ! isset(self::$items[self::CORE_SLUG]['name']) ){
self::$items[self::CORE_SLUG]['name'] = rey__get_props('core_title');
}
endif;
return self::$items;
}
/**
* Retrieve major version
*
* @param string $version
* @return string
*/
public static function get_item_major_version( $version ){
$v = array_map('absint', explode( '.', $version ));
return sprintf('%d.%d.0', $v[0], $v[1]);
}
public static function get_config( $slug, $items ){
$sibling = self::CORE_SLUG === $slug ? self::THEME_SLUG : self::CORE_SLUG;
if( ! version_compare( constant($items[ $sibling ]['version_constant']), $items[ $slug ]['major_version'], '<' ) ){
return [];
}
return [
'to_update' => $items[ $sibling ]['name'],
'from' => $items[ $slug ]['name'],
'major_version' => $items[ $slug ]['major_version'],
'to_update_version' => constant($items[ $sibling ]['version_constant']),
'slug' => $sibling,
'type' => $items[ $sibling ]['type']
];
}
public function render_notice(){
$items = self::get_items();
$maybe_load_assets = [];
foreach ( array_keys($items) as $slug ) {
$config = self::get_config($slug, $items);
if( empty($config) ){
continue;
}
$maybe_load_assets[] = true;
$sync_error = sprintf(
__('<p><strong>%s</strong> is outdated and not in sync with <strong>%s</strong>.</p>', 'rey-core'),
$config['to_update'],
$config['from']
);
$sync_error .= sprintf(
__('<p>The minimum %1$s version should be <strong>%2$s</strong> (but currently it\'s %3$s). If the site is not running the latest major version, there could be issues or errors because the <em>Core plugin</em> depends on the <em>Theme</em> in various aspects. Please <a href="%4$s" target="_blank"><u>follow this article</u></a> which explains more about this problem.', 'rey-core'),
$config['to_update'],
$config['major_version'],
$config['to_update_version'],
function_exists('rey__support_url') ? rey__support_url('kb/rey-theme-is-outdated-and-not-in-sync-with-rey-core-error/') : ''
);
$nonce = wp_create_nonce( 'updates' );
$sync_error .= sprintf('<p id="outofsync-error-actions" style="display:none" data-slug="%s" data-nonce="%s"><span class="spinner"></span>', $config['slug'], $nonce);
$sync_error .= sprintf(
__('<strong><u><a href="#" data-slug="%2$s" data-nonce="%3$s" class="rey-genericBtn" id="js-update-link">Update %1$s now</a></u></strong>', 'rey-core'),
$config['to_update'],
$config['slug'],
$nonce
);
$sync_error .= ' or ';
$sync_error .= sprintf(
__('<strong><u><a href="#" data-slug="%1$s" data-nonce="%2$s" class="rey-genericBtn" id="js-update-download-link">download</a></u></strong> and manually install it like you would install a new %3$s.', 'rey-core'),
$config['slug'],
$nonce,
$config['type']
);
$sync_error .= '</p>';
$sync_error .= '<p id="outofsync-error-spinner"></p>';
$sync_error .= __('<p><small>This message only shows to administrators and is not public.</small></p>', 'rey-core');
printf('<div class="rey-overlay --no-close" id="outofsync-error--overlay"></div><div id="outofsync-error">%s</div>', $sync_error);
}
if( in_array(true, $maybe_load_assets, true) ){
$this->print_js();
$this->print_css();
}
}
public static function ajax_get_data(){
if ( ! wp_verify_nonce( $_POST['_ajax_nonce'], 'updates' ) ) {
return ['error' => 'Operation not allowed!'];
}
if( ! (current_user_can('administrator') || current_user_can('install_plugins')) ){
return ['error' => 'Operation not allowed!'];
}
if( ! function_exists('rey__clean') ){
return ['error' => 'Missing clean function.'];
}
if( ! (isset($_POST['slug']) && $slug = rey__clean($_POST['slug'])) ){
return ['error' => 'Missing type.'];
}
$items = self::get_items();
if( ! isset($items[$slug]) ){
return ['error' => 'Incorrect slug.'];
}
return [
'slug' => $slug,
'items' => $items,
];
}
public function ajax__outdated_link(){
$data = self::ajax_get_data();
if( isset($data['error']) ){
wp_send_json_error( $data['error'] );
}
if( ! class_exists('\Rey\Upgrader') ){
require_once __DIR__ . '/upgrader.php';
}
$upgrader = new \Rey\Upgrader([
'slug' => $data['slug'],
'basename' => $data['items'][ $data['slug'] ]['basename'],
'hook' => 'rey/update/outofsync',
]);
if( is_array($upgrader) && isset($upgrader['error']) ){
wp_send_json_error( $upgrader['error'] );
}
wp_send_json_success( $upgrader );
}
/**
* Handles downloading the lastest version
*
* @return void
*/
public function ajax__download_link(){
$data = self::ajax_get_data();
if( isset($data['error']) ){
wp_send_json_error( $data['error'] );
}
if( ! ($download_link = self::get_download_url( $data['slug'] ) ) ){
wp_send_json_error( 'Cannot retrieve download link.' );
}
wp_send_json_success($download_link);
}
public static function get_download_url( $slug ){
if( ! class_exists('\ReyTheme_API') ){
return false;
}
return \ReyTheme_API::getInstance()->get_download_url( self::THEME_SLUG === $slug ? 'theme' : $slug );
}
/**
* Handles downloading the lastest version
*
* @return void
*/
public function ajax__get_actions(){
$data = self::ajax_get_data();
if( isset($data['error']) ){
wp_send_json_error( $data['error'] );
}
if( ! class_exists('\ReyTheme_API') ){
wp_send_json_error();
}
$url = add_query_arg([
'purchase_code' => \ReyTheme_Base::get_purchase_code()
], \ReyTheme_API::API_SITE_URL
);
if( ! self::valid_url( $url ) ){
wp_send_json_error();
}
wp_send_json_success();
}
/**
* Render CSS for handling the notice styling
*
* @return void
*/
public function print_css(){
?>
<style type="text/css">
body:not(.wp-admin) #outofsync-error--overlay {
visibility: visible;
opacity: 1;
pointer-events: auto;
left: 0;
cursor: auto;
}
#outofsync-error {
--mleft: 2px;
--mright: 20px;
position: relative;
margin: 25px var(--mright) 15px var(--mleft);
padding: 35px 40px 30px;
font-size: .9rem;
background-color: HSL(var(--neutral-0));
border: 2px solid HSL(var(--neutral-3));
color: HSL(var(--neutral-7));
box-shadow: var(--b-shadow-5);
border-radius: 7px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 400;
}
@media (min-width: 1025px) {
#outofsync-error {
--left-padding: 160px;
padding-left: var(--left-padding);
}
}
#outofsync-error a {
color: HSL(var(--neutral-9));
}
#outofsync-error a:hover {
text-decoration: none;
}
#outofsync-error p {
font-size: 1em;
margin-top: 0;
margin-bottom: 1.2em;
}
#outofsync-error p:last-of-type {
margin-bottom: 0;
}
#outofsync-error-spinner:after {
content: "";
display: inline-block;
width: 1em;
height: 1em;
border: 2px solid transparent;
border-right-color: currentColor;
border-bottom-color: currentColor;
border-radius: 50%;
vertical-align: baseline;
animation: spinner-border .75s linear infinite;
opacity: .5;
}
@keyframes spinner-border {
to {
transform: rotate(360deg);
}
}
@media (min-width: 1025px) {
#outofsync-error:before {
content: "\f534";
font-family: 'dashicons';
position: absolute;
top: calc(50% - 0.5em);
left: calc((var(--left-padding) / 2) - .5em);
font-size: 80px;
line-height: 1;
opacity: .15;
}
}
body:not(.wp-admin) #outofsync-error {
--mleft: auto;
--mright: auto;
max-width: var(--container-max-width);
width: 85%;
position: fixed;
z-index: 9999;
top: 60px;
left: 50%;
transform: translateX(-50%);
}
@media (min-width: 1025px) {
body:not(.wp-admin) #outofsync-error {
width: 70%;
top: 100px;
}
}
</style>
<?php
}
/**
* Render JS for handling the notice actions
*
* @return void
*/
public function print_js(){
?>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
var notice = document.getElementById('outofsync-error');
if( ! notice ){
return;
}
var responseHolder = notice.querySelector('#outofsync-error-actions');
if( responseHolder && typeof jQuery !== 'undefined'){
jQuery.ajax({
method: "post",
url: '<?php echo admin_url( 'admin-ajax.php' ) ?>',
data: {
action : "rey_get_actions",
slug : responseHolder.getAttribute('data-slug'),
_ajax_nonce: responseHolder.getAttribute('data-nonce'),
},
success: function (response) {
var spinner = notice.querySelector('#outofsync-error-spinner');
if( spinner ){
spinner.remove();
}
if( response.success ){
responseHolder.style.display = 'block';
}
},
});
}
var updateLink = notice.querySelector('#js-update-link');
var isUpdating = false;
window.onbeforeunload = function () {
if (isUpdating) {
return "Updating is in progress! Please don't close window.";
}
return undefined;
};
updateLink && updateLink.addEventListener('click', function(e){
e.preventDefault();
var btn = e.currentTarget;
isUpdating = true;
btn.classList.add('--disabled');
btn.classList.add('--loading');
btn.textContent = 'Updating..';
if( typeof jQuery === 'undefined' ){
console.error('jQuery not defined.')
return;
}
jQuery.ajax({
method: "post",
url: '<?php echo admin_url( 'admin-ajax.php' ) ?>',
data: {
action : "rey_outdated_link",
slug : btn.getAttribute('data-slug'),
_ajax_nonce: btn.getAttribute('data-nonce'),
},
success: function (response) {
btn.classList.remove('--loading');
btn.classList.remove('--disabled');
btn.textContent = 'Reloading page..';
isUpdating = false;
setTimeout(function () {
location.reload();
}, 1000);
},
});
});
var downloadLink = notice.querySelector('#js-update-download-link');
downloadLink && downloadLink.addEventListener('click', function(e){
e.preventDefault();
var btn = e.currentTarget;
btn.classList.add('--disabled');
btn.classList.add('--loading');
if( typeof jQuery === 'undefined' ){
console.error('jQuery not defined.')
return;
}
jQuery.ajax({
method: "post",
url: '<?php echo admin_url( 'admin-ajax.php' ) ?>',
data: {
action : "rey_download_link",
slug : btn.getAttribute('data-slug'),
_ajax_nonce: btn.getAttribute('data-nonce'),
},
success: function (response) {
btn.classList.remove('--loading');
if( ! response ){
console.error(response);
return;
}
if( ! response.success ){
btn.textContent = response.data;
console.error(response.data);
return;
}
btn.classList.remove('--disabled');
var a = document.createElement('a');
a.href = response.data;
a.download = btn.getAttribute('data-slug');
a.dispatchEvent(new MouseEvent('click'));
},
});
});
});
</script>
<?php
}
public static function valid_url($url)
{
$response = wp_safe_remote_get( $url );
if ( is_wp_error( $response ) ) {
return false;
}
return 200 === wp_remote_retrieve_response_code( $response );
}
}
new OutOfSync;