You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

366 lines
12 KiB
PHP

<?php
if ( ! defined( 'ET_CLOUD_SERVER_URL' ) ) {
define( 'ET_CLOUD_SERVER_URL', 'https://cloud.elegantthemes.com' );
}
class ET_Cloud_App {
/**
* @var ET_Cloud_App
*/
private static $_instance;
/**
* Get the class instance.
*
* @since 3.0.99
*
* @return ET_Builder_Library
*/
public static function instance() {
if ( ! self::$_instance ) {
self::$_instance = new self;
}
add_action( 'wp_ajax_et_cloud_update_tokens', array( 'ET_Cloud_App', 'ajaxRefreshTokens' ) );
add_action( 'wp_ajax_et_cloud_remove_tokens', array( 'ET_Cloud_App', 'removeTokens' ) );
add_filter( 'et_builder_load_requests', array( 'ET_Cloud_App', 'updateAjaxCallsList' ) );
return self::$_instance;
}
public static function updateAjaxCallsList() {
return array( 'action' => array( 'et_cloud_update_tokens' ) );
}
public static function removeTokens() {
$nonce = $_POST['et_cloud_token_nonce'];
if ( ! wp_verify_nonce( $nonce, 'et_cloud_remove_token' ) ) {
die();
}
$user_id = (string) get_current_user_id();
$saved_tokens = get_option( 'et_cloud_refresh_token', array() );
$saved_tokens[ $user_id ] = array();
// Save empty refresh token for current user.
update_option( 'et_cloud_refresh_token', $saved_tokens );
wp_send_json_success();
}
public static function ajaxRefreshTokens() {
$nonce = $_POST['et_cloud_token_nonce'];
if ( ! wp_verify_nonce( $nonce, 'et_cloud_refresh_token' ) ) {
die();
}
return ET_Cloud_App::refreshTokens();
}
public static function refreshTokens() {
// Clear options cache to make sure we're using the latest version of the token.
wp_cache_delete( 'et_cloud_refresh_token', 'options' );
$user_id = (string) get_current_user_id();
$saved_tokens = get_option( 'et_cloud_refresh_token', array() );
$access_token = sanitize_text_field( $_POST['et_cloud_access_token'] );
$save_session = wp_validate_boolean( sanitize_text_field( $_POST['et_cloud_save_session'] ) );
$token_part = sanitize_text_field( $_POST['et_cloud_refresh_token_part'] );
$user_token_data = isset( $saved_tokens[ $user_id ] ) ? $saved_tokens[ $user_id ] : array();
$is_refresh = ! $access_token || '' === $access_token;
$url = ET_CLOUD_SERVER_URL . '/wp/wp-json/cloud/v1/activate';
$refresh_token = '';
if ( $is_refresh && is_array( $user_token_data ) && !empty( $user_token_data ) ) {
$refresh_token = $user_token_data['is_full_token'] ? $user_token_data['refresh_token'] : $user_token_data['refresh_token'] . $token_part;
$save_session = $is_refresh ? $user_token_data['is_full_token'] : $save_session;
}
if ( $is_refresh ) {
if ( ! $save_session && ( ! $token_part || '' === $token_part ) ) {
wp_send_json_error( array(
'error' => '401',
'errorType' => 'silent',
) );
return;
}
$is_updating_token = 'updating' === get_transient( 'et_cloud_access_token_update_status' );
// Previous request is not finished yet. Try again after 2 seconds.
// Otherwise this request will fail with 401 error.
if ( $is_updating_token ) {
sleep(2);
ET_Cloud_App::refreshTokens();
}
// Set updating token flag with 5 seconds expiration.
set_transient( 'et_cloud_access_token_update_status', 'updating', 5 );
}
if ( ( ! $is_refresh && '' === $access_token ) || ( $is_refresh && '' === $refresh_token ) ) {
wp_send_json_error( array(
'error' => '401',
) );
return;
}
if ( $is_refresh ) {
$token_array = explode('.', $refresh_token);
// Token is a json string of base64 encoded array. Decode it to access the data in token.
$refresh_token_data = json_decode(base64_decode($token_array[1]));
if ( !empty( $refresh_token_data ) && is_object( $refresh_token_data ) && isset( $refresh_token_data->aud ) ) {
$user_cloud_endpoint = $refresh_token_data->aud[1];
} else {
wp_send_json_error( array(
'error' => '401',
) );
return;
}
$url = sprintf('%1$s/wp-json/auth/v1/token', $user_cloud_endpoint);
}
if ( ! $is_refresh ) {
update_option( 'et_server_domain_token', $access_token );
}
$request_body = array();
$auth_token = $is_refresh ? $refresh_token : $access_token;
$response = wp_remote_post( $url, array(
'headers' => array(
'Authorization' => 'Bearer ' . $auth_token,
'X-ET-ORIGIN' => site_url(),
),
'body' => $request_body,
) );
if ( is_wp_error( $response ) ) {
// Delete updating token flag.
delete_transient( 'et_cloud_access_token_update_status' );
wp_send_json_error( array(
'error' => 'Cloud Request Failed. Please Try Again Later',
) );
return;
}
$response_code = wp_remote_retrieve_response_code( $response );
$response_body = wp_remote_retrieve_body( $response );
$decoded_body = json_decode( $response_body, TRUE );
// Valid response should contain 2 tokens.
if ( !isset( $decoded_body['refresh_token'] ) || !isset( $decoded_body['access_token'] ) || 200 !== $response_code ) {
// Authorization error. Need to reset all tokens and ask user to login again.
if ( 401 === $response_code ) {
$saved_tokens[ $user_id ] = array();
// Save empty refresh token for current user.
update_option( 'et_cloud_refresh_token', $saved_tokens, false );
// Delete updating token flag.
delete_transient( 'et_cloud_access_token_update_status' );
wp_send_json_error( array(
'error' => '401',
) );
return;
}
wp_send_json_error( array(
'error' => wp_remote_retrieve_response_message( $response ),
) );
return;
}
$refresh_token = $decoded_body['refresh_token'];
$access_token = $decoded_body['access_token'];
$token_to_save = $refresh_token;
$token_part = '';
// We shouldn't save the full token, so user cannot use this token in other browser.
if ( ! $save_session ) {
$token_length = (int) strlen( $refresh_token );
$token_parts = str_split( $refresh_token, ceil( $token_length / 2 ) );
$token_to_save = $token_parts[0];
$token_part = $token_parts[1];
}
$saved_tokens[ $user_id ] = array(
'refresh_token' => sanitize_text_field( $token_to_save ),
'is_full_token' => $save_session,
);
// Save refresh token for current user.
update_option( 'et_cloud_refresh_token', $saved_tokens, false );
// Delete updating token flag.
delete_transient( 'et_cloud_access_token_update_status' );
// Save Access Token for 30 seconds so it can be quickly retrieved by the VB.
set_transient( 'et_cloud_access_token', $access_token, 30);
wp_send_json_success( array(
'accessToken' => $decoded_body['access_token'],
'refreshTokenPart' => $token_part,
'domainToken' => get_option( 'et_server_domain_token', '' ),
'sharedFolders' => self::normalize_shared_cloud_array( $decoded_body['clouds'] ),
) );
}
/**
* Normalize shared cloud array from the server response.
*
* @param array $shared_cloud_array Raw shared clouds array.
*
* @return array
*/
public static function normalize_shared_cloud_array( $shared_cloud_array ) {
if ( empty( $shared_cloud_array ) ) {
return null;
}
$normalized_array = array();
foreach ( $shared_cloud_array as $cloud_id => $shared_cloud ) {
$use_permission = isset( $shared_cloud['permissions']['use_items'] ) ? $shared_cloud['permissions']['use_items'] : false;
$add_permission = isset( $shared_cloud['permissions']['add_items'] ) ? $shared_cloud['permissions']['add_items'] : false;
$edit_permission = isset( $shared_cloud['permissions']['edit_items'] ) ? $shared_cloud['permissions']['edit_items'] : false;
$delete_permission = isset( $shared_cloud['permissions']['delete_items'] ) ? $shared_cloud['permissions']['delete_items'] : false;
// No permission to use this cloud.
if ( ! $use_permission && ! $add_permission && ! $edit_permission && ! $delete_permission ) {
continue;
}
$normalized_array[] = array(
'id' => $cloud_id,
'name' => $shared_cloud['owner'],
'count' => $shared_cloud['item_counts'],
'endpoint' => $shared_cloud['endpoint'],
'permissions' => array(
'use' => $shared_cloud['permissions']['use_items'],
'add' => $shared_cloud['permissions']['add_items'],
'edit' => $shared_cloud['permissions']['edit_items'],
'delete' => $shared_cloud['permissions']['delete_items'],
),
);
}
return $normalized_array;
}
public static function hasRefreshToken() {
$user_id = (string) get_current_user_id();
$saved_tokens = get_option( 'et_cloud_refresh_token', array() );
$refresh_token = isset( $saved_tokens[ $user_id ] ) ? $saved_tokens[ $user_id ] : array();
return $refresh_token && ! empty( $refresh_token );
}
public static function get_cloud_helpers() {
if ( !defined( 'ET_CLOUD_PLUGIN_DIR' ) ) {
define( 'ET_CLOUD_PLUGIN_DIR', get_template_directory() . '/cloud' );
}
$home_url = wp_parse_url( get_site_url() );
$etAccount = et_core_get_et_account();
return [
'i18n' => require ET_CLOUD_PLUGIN_DIR . '/i18n/library.php',
'nonces' => [
'et_cloud_download_item' => wp_create_nonce( 'et_cloud_download_item' ),
'et_cloud_refresh_token' => wp_create_nonce( 'et_cloud_refresh_token' ),
'et_cloud_remove_token' => wp_create_nonce( 'et_cloud_remove_token' ),
'et_builder_split_library_item' => wp_create_nonce( 'et_builder_split_library_item' ),
'et_builder_ajax_save_domain_token' => wp_create_nonce( 'et_builder_ajax_save_domain_token' ),
'et_builder_marketplace_api_get_layouts' => wp_create_nonce( 'et_builder_marketplace_api_get_layouts' ),
'et_builder_marketplace_api_get_layout_categories' => wp_create_nonce( 'et_builder_marketplace_api_get_layout_categories' ),
],
'ajaxurl' => is_ssl() ? admin_url( 'admin-ajax.php' ) : admin_url( 'admin-ajax.php', 'http' ),
'home_url' => isset( $home_url['path'] ) ? untrailingslashit( $home_url['path'] ) : '/',
'website_url' => $home_url['host'],
'predefined_items_url' => ET_CLOUD_SERVER_URL . '/wp/wp-json/cloud/v1',
'etAccount' => [
'username' => $etAccount['et_username'],
'apiKey' => $etAccount['et_api_key'],
],
'domainToken' => get_option( 'et_server_domain_token', '' ),
'initialCloudStatus' => self::hasRefreshToken() ? 'on' : 'off',
'localCategoriesEdit' => current_user_can( 'manage_categories' ) ? 'allowed' : 'notAllowed',
];
}
/**
* Load the Cloud App scripts.
*
* @since ??
*
* @return void
*/
public static function load_js( $enqueue_prod_scripts = true, $skip_react_loading = false ) {
if ( !defined( 'ET_CLOUD_PLUGIN_URI' ) ) {
define( 'ET_CLOUD_PLUGIN_URI', get_template_directory_uri() . '/cloud' );
}
if ( !defined( 'ET_CLOUD_PLUGIN_DIR' ) ) {
define( 'ET_CLOUD_PLUGIN_DIR', get_template_directory() . '/cloud' );
}
$CORE_VERSION = defined( 'ET_CORE_VERSION' ) ? ET_CORE_VERSION : '';
$ET_DEBUG = defined( 'ET_DEBUG' ) && ET_DEBUG;
$DEBUG = $ET_DEBUG;
$home_url = wp_parse_url( get_site_url() );
$build_dir_uri = ET_CLOUD_PLUGIN_URI . '/build';
$common_scripts = ET_COMMON_URL . '/scripts';
$cache_buster = $DEBUG ? mt_rand() / mt_getrandmax() : $CORE_VERSION;
$asset_path = ET_CLOUD_PLUGIN_DIR . '/build/et-cloud-app.bundle.js';
if ( file_exists( $asset_path ) ) {
wp_enqueue_style( 'et-cloud-styles', "{$build_dir_uri}/et-cloud-app.bundle.modals.css", [], (string) $cache_buster );
}
wp_enqueue_script( 'es6-promise', "{$common_scripts}/es6-promise.auto.min.js", [], '4.2.2', true );
$BUNDLE_DEPS = [
'jquery',
'react',
'react-dom',
'es6-promise',
];
if ( $DEBUG || $enqueue_prod_scripts || file_exists( $asset_path ) ) {
$BUNDLE_URI = ! file_exists( $asset_path ) ? "{$home_url['scheme']}://{$home_url['host']}:31495/et-cloud-app.bundle.js" : "{$build_dir_uri}/et-cloud-app.bundle.js";
// Skip the React loading if we already have React ( Gutenberg editor for example ) to avoid conflicts.
if ( ! $skip_react_loading ) {
if ( function_exists( 'et_fb_enqueue_react' ) ) {
et_fb_enqueue_react();
}
}
wp_enqueue_script( 'et-cloud-app', $BUNDLE_URI, $BUNDLE_DEPS, (string) $cache_buster, true );
wp_localize_script( 'et-cloud-app', 'et_cloud_data', ET_Cloud_App::get_cloud_helpers());
}
}
}
ET_Cloud_App::instance();