| Server IP : 146.59.209.152 / Your IP : 216.73.216.152 Web Server : Apache System : Linux webm009.cluster131.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64 User : monpetu ( 144298) PHP Version : 7.4.33 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/monpetu/www/ipprint/wp-content/plugins/cartflows/classes/ |
Upload File : |
<?php
/**
* Update Compatibility
*
* @package CartFlows
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'Cartflows_Update' ) ) :
/**
* CartFlows Update initial setup
*
* @since 1.0.0
*/
class Cartflows_Update {
/**
* Class instance.
*
* @access private
* @var Cartflows_Update|null
*/
private static $instance;
/**
* Initiator
*
* @return Cartflows_Update
*/
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
public function __construct() {
add_action( 'admin_init', array( $this, 'init' ) );
}
/**
* Init
*
* @since 1.0.0
* @return void
*/
public function init() {
do_action( 'cartflows_update_before' );
// Get auto saved version number.
$saved_version = get_option( 'cartflows-version', false );
// Update auto saved version number.
if ( ! $saved_version ) {
update_option( 'cartflows-version', CARTFLOWS_VER );
return;
}
// If equals then return.
if ( version_compare( $saved_version, CARTFLOWS_VER, '=' ) ) {
return;
}
$this->logger_files();
if ( version_compare( $saved_version, '1.1.22', '<' ) ) {
update_option( 'wcf_setup_skipped', true );
}
if ( version_compare( $saved_version, '1.2.0', '<' ) ) {
$this->changed_wp_templates();
}
/* Add legacy admin option */
if ( version_compare( $saved_version, '1.6.0', '<' ) ) {
update_option( 'cartflows-old-ui-user', true );
update_option( 'cartflows-legacy-admin', true );
update_option( 'cartflows-legacy-meta-show-design-options', true );
}
/* Updating meta for global checkout migration & Permalinks */
if ( version_compare( $saved_version, '1.10.0', '<' ) ) {
$global_checkout = \Cartflows_Helper::get_common_setting( 'global_checkout' );
if ( $global_checkout ) {
$flow_id = wcf()->utils->get_flow_id_from_step_id( $global_checkout );
if ( $flow_id ) {
update_option( '_cartflows_store_checkout', $flow_id );
update_option( '_cartflows_old_global_checkout', $global_checkout );
delete_post_meta( $global_checkout, 'wcf-checkout-products' );
}
}
update_option( 'cartflows_show_weekly_report_email_notice', 'yes' );
$permalink_settings = Cartflows_Helper::get_admin_settings_option( '_cartflows_permalink', false, false );
if ( ! $permalink_settings ) {
$default_settings = array(
'permalink' => CARTFLOWS_STEP_POST_TYPE,
'permalink_flow_base' => CARTFLOWS_FLOW_POST_TYPE,
'permalink_structure' => '',
);
Cartflows_Helper::update_admin_settings_option( '_cartflows_permalink', $default_settings );
}
}
// Update required license key to lowercase. Can be removed after 3 major update.
if ( version_compare( $saved_version, '1.11.1', '<' ) && _is_cartflows_pro() ) {
$api_key = get_option( 'wc_am_client_CartFlows_api_key' );
$api_key_data = get_option( 'wc_am_client_CartFlows' );
// Take backup of options.
update_option(
'cartflows_license_backup_data',
array(
'wc_am_client_cartflows_api_key' => $api_key,
'wc_am_client_cartflows' => $api_key_data,
)
);
delete_option( 'wc_am_client_CartFlows_api_key' );
delete_option( 'wc_am_client_CartFlows' );
// Delete the cached value for old user ( before v1.11.0 and after v1.11.0 ) so WP can add new value.
wp_cache_flush();
if ( $api_key_data ) {
$new_data = array(
'wc_am_client_cartflows_api_key' => isset( $api_key_data['wc_am_client_CartFlows_api_key'] ) ? $api_key_data['wc_am_client_CartFlows_api_key'] : $api_key_data['wc_am_client_cartflows_api_key'],
);
update_option( 'wc_am_client_cartflows', $new_data );
}
if ( $api_key ) {
update_option( 'wc_am_client_cartflows_api_key', $api_key );
}
}
// Set default page builder to "Other" for those who have set it as "DIVI".
if ( version_compare( $saved_version, '2.0.0', '<' ) ) {
$common_settings = get_option( '_cartflows_common', array() );
if ( is_array( $common_settings ) && 'divi' === $common_settings['default_page_builder'] ) {
$common_settings['default_page_builder'] = 'elementor';
update_option( '_cartflows_common', $common_settings );
}
}
// Migrate old combined custom script meta to separate JS and CSS meta fields.
if ( version_compare( $saved_version, '2.2.2', '<' ) ) {
$this->migrate_custom_scripts();
}
// 3.0 ships a redesigned admin UI. Existing users (saved_version < 3.0.0) keep the
// legacy admin until they opt into the new UI from the in-app notice or the
// Advanced-tab toggle. Saved as 'enable' to match the toggle's backComp format
// so the field round-trips correctly. Fresh installs short-circuit at the
// `! $saved_version` early return above and default to the new UI.
if ( is_string( $saved_version ) && version_compare( $saved_version, '3.0.0', '<' ) ) {
update_option( 'cartflows-legacy-admin', 'enable' );
}
// Update auto saved version number.
update_option( 'cartflows-version', CARTFLOWS_VER );
// Update cartflows asset version to regenerate the dynamic css. We are using the time() function to add the random number.
update_option( 'cartflows-assets-version', time() );
do_action( 'cartflows_update_after' );
}
/**
* Loading logger files.
*
* @since 1.0.0
* @return void
*/
public function logger_files() {
if ( ! defined( 'CARTFLOWS_LOG_DIR' ) ) {
$upload_dir = wp_upload_dir( null, false );
define( 'CARTFLOWS_LOG_DIR', $upload_dir['basedir'] . '/cartflows-logs/' );
}
wcf()->create_files();
}
/**
* Set script migration status.
*
* @param string $status Migration status value.
* @since 2.2.2
* @return void
*/
public function set_script_migration_status( $status ) {
update_option( 'cartflows_script_migration_status', $status );
}
/**
* Determine if the migration notice should be shown.
*
* Returns false if migration is completed or accepted.
* Returns false if status is 'skipped' and 7 days have not yet passed.
* Returns false if no old script values exist (auto-sets status to 'completed').
*
* @since 2.2.2
* @return bool
*/
public function should_show_migration_notice() {
$status = \CartFlows_Helper::get_script_migration_status();
if ( 'completed' === $status || 'accepted' === $status ) {
return false;
}
if ( 'skipped' === $status ) {
$skip_timestamp = (int) get_option( 'cartflows_script_migration_skip_timestamp', 0 );
// Show again only after 7 days have passed since last skip.
if ( ( time() - $skip_timestamp ) < WEEK_IN_SECONDS ) {
return false;
}
}
// Check cached result first to avoid running the meta query on every page load.
$has_old_scripts = get_option( 'cartflows_has_old_custom_scripts', '' );
if ( '' === $has_old_scripts ) {
// First time check — run the query once and cache the result.
$has_old_scripts = $this->has_old_custom_scripts() ? 'yes' : 'no';
update_option( 'cartflows_has_old_custom_scripts', $has_old_scripts, true );
}
// If no old script values exist, auto-complete migration and enable the code editor.
if ( 'yes' !== $has_old_scripts ) {
$this->set_script_migration_status( 'completed' );
return false;
}
return true;
}
/**
* Check if any posts have old combined custom script meta values.
*
* Queries the database for any cartflows_step or cartflows_flow posts
* that have non-empty 'wcf-custom-script' or 'wcf-flow-custom-script' meta.
*
* @since 2.2.3
* @return bool True if old script data exists, false otherwise.
*/
public function has_old_custom_scripts() {
global $wpdb;
$result = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"SELECT 1 FROM {$wpdb->postmeta} pm
INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID
WHERE p.post_type IN (%s, %s)
AND pm.meta_key IN (%s, %s)
AND pm.meta_value != ''
LIMIT 1",
'cartflows_step',
'cartflows_flow',
'wcf-custom-script',
'wcf-flow-custom-script'
)
);
return ( null !== $result );
}
/**
* Get current migration skip count.
*
* @since 2.2.2
* @return int
*/
public function get_migration_skip_count() {
return (int) get_option( 'cartflows_script_migration_skip_count', 0 );
}
/**
* Increment migration skip count and record the skip timestamp.
*
* @since 2.2.2
* @return void
*/
public function increment_migration_skip_count() {
$count = $this->get_migration_skip_count();
update_option( 'cartflows_script_migration_skip_count', $count + 1 );
update_option( 'cartflows_script_migration_skip_timestamp', time() );
}
/**
* Run on-demand migration of custom scripts triggered by user action.
*
* Unlike migrate_custom_scripts() which runs during version updates,
* this method is called only when the user explicitly clicks "Migrate Data"
* in the admin notice. It returns a count of migrated posts and updates
* the migration status to 'completed'.
*
* @since 2.2.2
* @return int Number of posts that had data migrated.
*/
public function migrate_custom_scripts_on_demand() {
$migrated_count = 0;
// Migrate step-level custom scripts.
$migrated_count += $this->migrate_post_type_scripts_counted(
'cartflows_step',
'wcf-custom-script',
'wcf-step-custom-js',
'wcf-step-custom-css'
);
// Migrate flow-level custom scripts.
$migrated_count += $this->migrate_post_type_scripts_counted(
'cartflows_flow',
'wcf-flow-custom-script',
'wcf-flow-custom-js',
'wcf-flow-custom-css'
);
$this->set_script_migration_status( 'completed' );
return $migrated_count;
}
/**
* Migrate custom scripts for a specific post type and return the count of migrated posts.
*
* @param string $post_type The post type to query.
* @param string $old_meta_key The old combined script meta key.
* @param string $new_js_meta_key The new JS-only meta key.
* @param string $new_css_meta_key The new CSS-only meta key.
*
* @since 2.2.2
* @return int Number of posts migrated.
*/
public function migrate_post_type_scripts_counted( $post_type, $old_meta_key, $new_js_meta_key, $new_css_meta_key ) {
global $wpdb;
$results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"SELECT p.ID, pm.meta_value FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
WHERE p.post_type = %s AND pm.meta_key = %s AND pm.meta_value != ''
LIMIT 500",
$post_type,
$old_meta_key
)
);
if ( empty( $results ) || ! is_array( $results ) ) {
return 0;
}
$migrated = 0;
foreach ( $results as $row ) {
$post_id = $row->ID;
$old_value = $row->meta_value;
// Skip if the new meta fields already have values (don't overwrite manual entries).
$existing_js = get_post_meta( $post_id, $new_js_meta_key, true );
$existing_css = get_post_meta( $post_id, $new_css_meta_key, true );
if ( ! empty( $existing_js ) || ! empty( $existing_css ) ) {
continue;
}
// Decode HTML entities first since the old value was stored encoded.
$decoded = html_entity_decode( $old_value, ENT_QUOTES, 'UTF-8' );
// Extract CSS and JS from the combined script.
$parsed = $this->parse_combined_script( $decoded );
$did_migrate = false;
if ( ! empty( $parsed['css'] ) ) {
update_post_meta( $post_id, $new_css_meta_key, htmlentities( $parsed['css'] ) );
$did_migrate = true;
}
if ( ! empty( $parsed['js'] ) ) {
update_post_meta( $post_id, $new_js_meta_key, htmlentities( $parsed['js'] ) );
$did_migrate = true;
}
if ( $did_migrate ) {
++$migrated;
}
}
return $migrated;
}
/**
* Migrate old combined custom script meta to separate JS and CSS meta fields.
*
* Previously, a single textarea accepted both JS and CSS with <script>/<style> tags.
* Now we have separate CodeMirror editors for JS and CSS, so we need to split the
* old combined value into the new separate meta fields.
*
* @since 2.2.2
* @return void
*/
public function migrate_custom_scripts() {
// Migrate step-level custom scripts.
$this->migrate_post_type_scripts(
'cartflows_step',
'wcf-custom-script',
'wcf-step-custom-js',
'wcf-step-custom-css'
);
// Migrate flow-level custom scripts.
$this->migrate_post_type_scripts(
'cartflows_flow',
'wcf-flow-custom-script',
'wcf-flow-custom-js',
'wcf-flow-custom-css'
);
}
/**
* Migrate custom scripts for a specific post type from old combined meta to separate JS/CSS meta.
*
* @param string $post_type The post type to query.
* @param string $old_meta_key The old combined script meta key.
* @param string $new_js_meta_key The new JS-only meta key.
* @param string $new_css_meta_key The new CSS-only meta key.
*
* @since 2.2.2
* @return void
*/
public function migrate_post_type_scripts( $post_type, $old_meta_key, $new_js_meta_key, $new_css_meta_key ) {
global $wpdb;
// Get all posts of this type that have the old custom script meta with a non-empty value.
$results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"SELECT p.ID, pm.meta_value FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
WHERE p.post_type = %s AND pm.meta_key = %s AND pm.meta_value != ''
LIMIT 500", // Limit to 500 posts per batch to avoid memory issues.
$post_type,
$old_meta_key
)
);
if ( empty( $results ) || ! is_array( $results ) ) {
return;
}
foreach ( $results as $row ) {
$post_id = $row->ID;
$old_value = $row->meta_value;
// Skip if the new meta fields already have values (don't overwrite manual entries).
$existing_js = get_post_meta( $post_id, $new_js_meta_key, true );
$existing_css = get_post_meta( $post_id, $new_css_meta_key, true );
if ( ! empty( $existing_js ) || ! empty( $existing_css ) ) {
continue;
}
// Decode HTML entities first since the old value was stored encoded.
$decoded = html_entity_decode( $old_value, ENT_QUOTES, 'UTF-8' );
// Extract CSS and JS from the combined script.
$parsed = $this->parse_combined_script( $decoded );
if ( ! empty( $parsed['css'] ) ) {
// Store encoded with htmlentities to match the FILTER_SCRIPT save format.
update_post_meta( $post_id, $new_css_meta_key, htmlentities( $parsed['css'] ) );
}
if ( ! empty( $parsed['js'] ) ) {
// Store encoded with htmlentities to match the FILTER_SCRIPT save format.
update_post_meta( $post_id, $new_js_meta_key, htmlentities( $parsed['js'] ) );
}
}
}
/**
* Parse a combined script string to extract CSS and JS portions.
*
* The old custom script field could contain:
* - <style>...CSS...</style> blocks
* - <script>...JS...</script> blocks
* - Bare JS code (no tags, treated as JS)
*
* @param string $content The combined script content (decoded).
* @return array Associative array with 'js' and 'css' keys.
*
* @since 2.2.2
*/
public function parse_combined_script( $content ) {
$css = '';
$js = '';
// Extract all <style>...</style> blocks as CSS.
if ( preg_match_all( '/<style[^>]*>(.*?)<\/style>/is', $content, $style_matches ) ) {
$css = trim( implode( "\n", $style_matches[1] ) );
}
// Extract all <script>...</script> blocks as JS.
if ( preg_match_all( '/<script[^>]*>(.*?)<\/script>/is', $content, $script_matches ) ) {
$js = trim( implode( "\n", $script_matches[1] ) );
}
// Remove all <style>...</style> and <script>...</script> blocks from the content.
$remaining = preg_replace( '/<style[^>]*>.*?<\/style>/is', '', $content );
if ( null === $remaining ) {
$remaining = '';
}
$remaining = preg_replace( '/<script[^>]*>.*?<\/script>/is', '', $remaining );
if ( null === $remaining ) {
$remaining = '';
}
$remaining = trim( $remaining );
// Any remaining content (not inside tags) is treated as JS.
if ( ! empty( $remaining ) ) {
$js = trim( $js . "\n" . $remaining );
}
return array(
'css' => $css,
'js' => $js,
);
}
/**
* Init
*
* @since 1.0.0
* @return void
*/
public function changed_wp_templates() {
global $wpdb;
$query_results = $wpdb->get_results(
$wpdb->prepare(
"SELECT {$wpdb->posts}.ID FROM {$wpdb->posts} LEFT JOIN {$wpdb->postmeta} ON ( {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id )
where {$wpdb->posts}.post_type = %s AND {$wpdb->postmeta}.meta_key = %s AND {$wpdb->postmeta}.meta_value != %s AND {$wpdb->postmeta}.meta_value != %s",
'cartflows_step',
'_wp_page_template',
'cartflows-canvas',
'cartflows-default'
)
); // db call ok; no-cache ok.
if ( is_array( $query_results ) && ! empty( $query_results ) ) {
require_once CARTFLOWS_DIR . 'classes/importer/batch-process/class-cartflows-change-template-batch.php';
wcf()->logger->log( '(✓) Update Templates BATCH Started!' );
$change_template_batch = new Cartflows_Change_Template_Batch();
foreach ( $query_results as $query_result ) {
wcf()->logger->log( '(✓) POST ID ' . $query_result->ID );
$change_template_batch->push_to_queue( $query_result->ID );
}
$change_template_batch->save()->dispatch();
}
}
}
/**
* Kicking this off by calling 'get_instance()' method
*/
Cartflows_Update::get_instance();
endif;