| 1 |
<?php |
| 2 |
defined( 'ABSPATH' ) || exit; // Exit if accessed directly |
| 3 |
|
| 4 |
/** |
| 5 |
* Class to initiate admin functionalists |
| 6 |
* Class WFFN_Admin |
| 7 |
*/ |
| 8 |
if ( ! class_exists( 'WFFN_Admin' ) ) { |
| 9 |
#[AllowDynamicProperties] |
| 10 |
class WFFN_Admin { |
| 11 |
|
| 12 |
private static $ins = null; |
| 13 |
private $funnel = null; |
| 14 |
public $fk_memory_limit = 256 * 1024 * 1024; |
| 15 |
private $step_against_fid = array(); |
| 16 |
private $step_count_against_fid = array(); |
| 17 |
|
| 18 |
/** |
| 19 |
* @var WFFN_Background_Importer $updater |
| 20 |
*/ |
| 21 |
public $wffn_updater; |
| 22 |
|
| 23 |
/** |
| 24 |
* WFFN_Admin constructor. |
| 25 |
*/ |
| 26 |
public function __construct() { |
| 27 |
|
| 28 |
/** Admin enqueue scripts*/ |
| 29 |
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_assets' ), 99 ); |
| 30 |
if ( $this->is_wffn_flex_page( 'all' ) ) { |
| 31 |
add_action( 'admin_enqueue_scripts', array( $this, 'js_variables' ), 0 ); |
| 32 |
} |
| 33 |
if ( $this->is_wffn_flex_page() ) { |
| 34 |
add_action( 'admin_enqueue_scripts', array( $this, 'maybe_register_breadcrumb_nodes' ), 5 ); |
| 35 |
} |
| 36 |
|
| 37 |
/** Add update count badge to FunnelKit menu - runs after core menu modifications */ |
| 38 |
add_action( 'admin_head', array( $this, 'add_menu_update_badge' ), 9999 ); |
| 39 |
/** |
| 40 |
* DB updates and table installation |
| 41 |
*/ |
| 42 |
$get_db_version = get_option( '_wffn_db_version', '0.0.0' ); |
| 43 |
|
| 44 |
if ( version_compare( WFFN_DB_VERSION, $get_db_version, '>' ) ) { |
| 45 |
add_action( 'admin_init', array( $this, 'check_db_version' ), 990 ); |
| 46 |
} |
| 47 |
add_action( 'admin_init', array( $this, 'maybe_update_database_update' ), 995 ); |
| 48 |
|
| 49 |
add_action( 'admin_init', array( $this, 'reset_wizard' ) ); |
| 50 |
if ( $this->is_wffn_flex_page() && ( ! isset( $_GET['path'] ) || '/user-setup' !== sanitize_text_field( wp_unslash( $_GET['path'] ) ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 51 |
add_action( 'admin_init', array( $this, 'maybe_force_redirect_to_wizard' ) ); |
| 52 |
} |
| 53 |
add_action( 'admin_head', array( $this, 'hide_from_menu' ) ); |
| 54 |
|
| 55 |
add_filter( 'get_pages', array( $this, 'add_landing_in_home_pages' ), 10, 2 ); |
| 56 |
add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 ); |
| 57 |
global $wffn_notices; |
| 58 |
if ( is_array( $wffn_notices ) || ! empty( $wffn_notices ) ) { |
| 59 |
add_action( 'admin_notices', array( $this, 'maybe_show_notices' ) ); |
| 60 |
} |
| 61 |
if ( $this->is_wffn_flex_page( 'all' ) ) { |
| 62 |
add_action( 'in_admin_header', array( $this, 'remove_all' ), - 1 ); |
| 63 |
} |
| 64 |
add_filter( 'plugin_action_links_' . WFFN_PLUGIN_BASENAME, array( $this, 'plugin_actions' ) ); |
| 65 |
|
| 66 |
/** Initiate Background updater if action scheduler is not available for template importing */ |
| 67 |
add_action( 'init', array( $this, 'wffn_maybe_init_background_updater' ), 110 ); |
| 68 |
add_filter( |
| 69 |
'bwf_general_settings_link', |
| 70 |
function () { |
| 71 |
return admin_url( 'admin.php?page=bwf&path=/funnels' ); |
| 72 |
}, |
| 73 |
100000 |
| 74 |
); |
| 75 |
add_filter( 'woofunnels_show_reset_tracking', '__return_true', 999 ); |
| 76 |
add_action( 'admin_head', array( $this, 'menu_highlight' ), 99999 ); |
| 77 |
add_action( 'pre_get_posts', array( $this, 'load_page_to_home_page' ), 9999 ); |
| 78 |
add_filter( 'bwf_settings_config_general', array( $this, 'settings_config' ) ); |
| 79 |
|
| 80 |
add_filter( 'bwf_experiment_ref_link', array( $this, 'maybe_modify_link' ), 10, 2 ); |
| 81 |
|
| 82 |
add_action( 'before_delete_post', array( $this, 'delete_funnel_step_permanently' ), 10, 2 ); |
| 83 |
add_filter( 'wffn_rest_get_funnel_steps', array( $this, 'maybe_delete_funnel_step' ), 10, 2 ); |
| 84 |
add_action( 'admin_bar_menu', array( $this, 'add_menu_in_admin_bar' ), 99 ); |
| 85 |
|
| 86 |
add_action( 'wffn_rest_plugin_activate_response', array( $this, 'maybe_add_auth_link_stripe' ), 10, 2 ); |
| 87 |
add_action( 'wffn_rest_plugin_activate_response', array( $this, 'maybe_add_auth_link_square' ), 10, 2 ); |
| 88 |
|
| 89 |
add_filter( 'woofunnels_global_settings', array( $this, 'add_global_setting_tabs' ), 5 ); |
| 90 |
|
| 91 |
add_filter( 'woofunnels_global_settings_fields', array( $this, 'add_settings_fields_array' ), 110 ); |
| 92 |
|
| 93 |
add_action( 'wp_ajax_wffn_blocks_incompatible_switch_to_classic', array( $this, 'blocks_incompatible_switch_to_classic_cart_checkout' ) ); |
| 94 |
add_action( 'wp_ajax_wffn_dismiss_notice', array( $this, 'ajax_dismiss_admin_notice' ) ); |
| 95 |
add_filter( |
| 96 |
'bwf_general_settings_default_config', |
| 97 |
function ( $config ) { |
| 98 |
if ( isset( $config['allow_theme_css'] ) ) { |
| 99 |
|
| 100 |
/** |
| 101 |
* Allow default theme script if user use any snippet |
| 102 |
*/ |
| 103 |
$allowed_themes = apply_filters( 'wffn_allowed_themes', array( 'flatsome', 'Extra', 'divi', 'Divi', 'astra', 'jupiterx', 'kadence' ) ); |
| 104 |
|
| 105 |
if ( function_exists( 'WFFN_Core' ) && ( ( is_array( $allowed_themes ) && in_array( get_template(), $allowed_themes, true ) ) || WFFN_Core()->page_builders->is_divi_theme_enabled() ) ) { |
| 106 |
$config['allow_theme_css'] = array( |
| 107 |
'wfacp_checkout', |
| 108 |
'wffn_ty', |
| 109 |
'wffn_landing', |
| 110 |
'wffn_optin', |
| 111 |
'wffn_oty', |
| 112 |
'wfocu_offer', |
| 113 |
); |
| 114 |
|
| 115 |
} |
| 116 |
} |
| 117 |
|
| 118 |
return $config; |
| 119 |
}, |
| 120 |
10, |
| 121 |
2 |
| 122 |
); |
| 123 |
|
| 124 |
if ( isset( $_GET['wfacp_id'] ) && isset( $_GET['new_ui'] ) && 'wffn' === $_GET['new_ui'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 125 |
add_action( 'init', array( $this, 'redirect_checkout_edit_link_on_new_ui' ) ); |
| 126 |
} |
| 127 |
if ( defined( 'FKCART_PLUGIN_FILE' ) ) { |
| 128 |
add_filter( |
| 129 |
'fkcart_app_header_menu', |
| 130 |
function ( $menu ) { |
| 131 |
if ( isset( $menu['analytics'] ) ) { |
| 132 |
return $menu; |
| 133 |
} |
| 134 |
|
| 135 |
$keys = array_keys( $menu ); |
| 136 |
$values = array_values( $menu ); |
| 137 |
|
| 138 |
$indexToInsert = array_search( 'templates', $keys, true ); |
| 139 |
|
| 140 |
array_splice( $keys, $indexToInsert, 0, 'analytics' ); |
| 141 |
array_splice( |
| 142 |
$values, |
| 143 |
$indexToInsert, |
| 144 |
0, |
| 145 |
array( |
| 146 |
'analytics' => array( |
| 147 |
'name' => 'Analytics', |
| 148 |
'link' => admin_url( 'admin.php?page=bwf&path=/analytics' ), |
| 149 |
), |
| 150 |
) |
| 151 |
); |
| 152 |
|
| 153 |
$resultArray = array_combine( $keys, $values ); |
| 154 |
if ( isset( $menu['settings'] ) ) { |
| 155 |
unset( $resultArray['settings'] ); |
| 156 |
} |
| 157 |
|
| 158 |
return $resultArray; |
| 159 |
} |
| 160 |
); |
| 161 |
} |
| 162 |
|
| 163 |
add_action( 'current_screen', array( $this, 'conditional_includes' ), 1 ); |
| 164 |
|
| 165 |
add_action( 'fk_optimize_conversion_table_analytics', array( $this, 'optimize_conversion_table_analytics' ) ); |
| 166 |
add_action( 'fk_remove_optimize_conversion_table_schedule', array( $this, 'remove_optimize_conversion_table_schedule' ) ); |
| 167 |
|
| 168 |
add_action( 'fk_fb_every_day', array( $this, 'check_memory_limit_errors' ), 9999 ); |
| 169 |
/** Email notification callback */ |
| 170 |
add_action( 'fk_fb_every_day', array( $this, 'maybe_setup_notification_schedule' ) ); |
| 171 |
add_action( 'wffn_performance_notification', array( $this, 'run_notifications' ) ); |
| 172 |
add_action( 'bwfan_run_midnight_cron', array( $this, 'maybe_setup_notification_schedule' ) ); |
| 173 |
if ( isset( $_GET['wffn_email_preview'] ) && current_user_can( 'administrator' ) ) { // @codingStandardsIgnoreLine |
| 174 |
add_action( 'admin_init', array( $this, 'test_notification_admin' ) ); |
| 175 |
} |
| 176 |
add_action( 'bwf_global_save_settings_funnelkit_notifications', array( $this, 'save_settings_for_email_notification' ), 10, 1 ); |
| 177 |
add_filter( 'use_block_editor_for_post', array( $this, 'maybe_enable_gutenberg_for_funnel_steps' ), PHP_INT_MAX, 2 ); |
| 178 |
|
| 179 |
/** Update-required surfaces while the installed Pro/Basic is older than WFFN_MIN_PRO_VERSION */ |
| 180 |
if ( is_admin() ) { |
| 181 |
add_action( 'admin_notices', array( $this, 'maybe_render_sitewide_notice' ) ); |
| 182 |
add_action( 'admin_init', array( $this, 'maybe_register_update_row_message' ) ); |
| 183 |
} |
| 184 |
add_action( 'admin_bar_menu', array( $this, 'maybe_add_admin_bar_state' ), 100 ); |
| 185 |
add_filter( 'wffn_admin_notifications', array( $this, 'maybe_have_outdated_notif' ) ); |
| 186 |
} |
| 187 |
|
| 188 |
/** |
| 189 |
* Enable Gutenberg editor for funnel steps that have Gutenberg design selected |
| 190 |
* |
| 191 |
* @param bool $use_block_editor Whether to use block editor |
| 192 |
* @param WP_Post $post The post object |
| 193 |
* @return bool |
| 194 |
*/ |
| 195 |
public function maybe_enable_gutenberg_for_funnel_steps( $use_block_editor, $post ) { |
| 196 |
try { |
| 197 |
// Define step types and their corresponding meta keys |
| 198 |
$step_meta_keys = array( |
| 199 |
'wffn_landing' => '_wflp_selected_design', |
| 200 |
'wffn_optin' => '_wfop_selected_design', |
| 201 |
'wffn_oty' => '_wfoty_selected_design', |
| 202 |
'wfacp_checkout' => '_wfacp_selected_design', |
| 203 |
'wffn_ty' => '_wftp_selected_design', |
| 204 |
'wfocu_offer' => '_wfocu_setting', |
| 205 |
); |
| 206 |
|
| 207 |
// Check if the post type is one of our funnel steps |
| 208 |
if ( isset( $step_meta_keys[ $post->post_type ] ) ) { |
| 209 |
// Get the post meta using the corresponding meta key |
| 210 |
$selected_design = get_post_meta( $post->ID, $step_meta_keys[ $post->post_type ], true ); |
| 211 |
|
| 212 |
// Handle different meta structures for different step types |
| 213 |
if ( 'wfocu_offer' === $post->post_type ) { |
| 214 |
// Upsell offers use template_group property for design type (object structure) |
| 215 |
if ( ! empty( $selected_design ) && isset( $selected_design->template_group ) && 'gutenberg' === $selected_design->template_group ) { |
| 216 |
return true; // Enable Gutenberg |
| 217 |
} |
| 218 |
} else { |
| 219 |
// Other step types use selected_type property (array structure) |
| 220 |
if ( ! empty( $selected_design ) && isset( $selected_design['selected_type'] ) && 'gutenberg' === $selected_design['selected_type'] ) { |
| 221 |
return true; // Enable Gutenberg |
| 222 |
} |
| 223 |
} |
| 224 |
} |
| 225 |
|
| 226 |
return $use_block_editor; // Default editor (Classic Editor) for all other cases |
| 227 |
} catch ( \Throwable $e ) { |
| 228 |
// Log error and return default behavior |
| 229 |
BWF_logger::get_instance()->log( 'Error in use_block_editor_for_post filter: ' . $e->getMessage(), 'wffn_admin' ); |
| 230 |
return $use_block_editor; |
| 231 |
} |
| 232 |
|
| 233 |
// Ensure we always return a value (fallback) |
| 234 |
return $use_block_editor; |
| 235 |
} |
| 236 |
|
| 237 |
/** |
| 238 |
* @return WFFN_Admin|null |
| 239 |
*/ |
| 240 |
public static function get_instance() { |
| 241 |
if ( null === self::$ins ) { |
| 242 |
self::$ins = new self(); |
| 243 |
} |
| 244 |
|
| 245 |
return self::$ins; |
| 246 |
} |
| 247 |
|
| 248 |
/** |
| 249 |
* Checks WooCommerce fatal error logs for memory limit issues from the previous day. |
| 250 |
* If found, updates the `fk_memory_limit` option if the memory size is below the configured limit. |
| 251 |
* Reads the log file using WP Filesystem and searches for memory-related error patterns. |
| 252 |
*/ |
| 253 |
public function check_memory_limit_errors() { |
| 254 |
global $wp_filesystem; |
| 255 |
try { |
| 256 |
$log_dir = wp_upload_dir()['basedir'] . '/wc-logs/'; |
| 257 |
if ( ! is_dir( $log_dir ) ) { |
| 258 |
return; |
| 259 |
} |
| 260 |
|
| 261 |
$yesterday = gmdate( 'Y-m-d', strtotime( '-1 day' ) ); |
| 262 |
|
| 263 |
$log_files = glob( $log_dir . "fatal-errors-{$yesterday}-*.log" ); |
| 264 |
|
| 265 |
if ( empty( $log_files ) ) { |
| 266 |
return; |
| 267 |
} |
| 268 |
|
| 269 |
$log_file = $log_files[0]; |
| 270 |
$log_contents = ''; |
| 271 |
|
| 272 |
if ( empty( $wp_filesystem ) ) { |
| 273 |
require_once ABSPATH . '/wp-admin/includes/file.php'; |
| 274 |
WP_Filesystem(); |
| 275 |
} |
| 276 |
|
| 277 |
if ( $wp_filesystem ) { |
| 278 |
$log_contents = $wp_filesystem->get_contents( $log_file ); |
| 279 |
} |
| 280 |
$log_lines = array_reverse( explode( "\n", $log_contents ) ); |
| 281 |
$memory_size = null; |
| 282 |
|
| 283 |
foreach ( $log_lines as $line ) { |
| 284 |
if ( preg_match( '/Allowed memory size of (\d+) bytes exhausted/', $line, $matches ) ) { |
| 285 |
$memory_size = isset( $matches[1] ) ? intval( $matches[1] ) : null; |
| 286 |
break; |
| 287 |
} elseif ( preg_match( '/Out of memory \(allocated (\d+)\)/', $line, $matches ) ) { |
| 288 |
$memory_size = isset( $matches[1] ) ? intval( $matches[1] ) : null; |
| 289 |
break; |
| 290 |
} |
| 291 |
} |
| 292 |
if ( $memory_size && $memory_size < $this->fk_memory_limit ) { |
| 293 |
update_option( 'fk_memory_limit', $memory_size, 'no' ); |
| 294 |
} |
| 295 |
} catch ( Exception |Error $e ) { |
| 296 |
return; |
| 297 |
} |
| 298 |
} |
| 299 |
|
| 300 |
public function add_global_setting_tabs( $menu ) { |
| 301 |
$f_tracking = array( |
| 302 |
'title' => __( 'First Party Tracking', 'funnel-builder' ), |
| 303 |
'slug' => 'funnelkit_first_party_tracking', |
| 304 |
'link' => apply_filters( 'bwf_general_settings_link', 'javascript:void(0)' ), |
| 305 |
'priority' => 6, |
| 306 |
'pro_tab' => true, |
| 307 |
); |
| 308 |
|
| 309 |
array_push( $menu, $f_tracking ); |
| 310 |
array_push( |
| 311 |
$menu, |
| 312 |
array( |
| 313 |
'title' => __( 'Pixel Tracking', 'funnel-builder' ), |
| 314 |
'slug' => 'funnelkit_pixel_tracking', |
| 315 |
'link' => apply_filters( 'bwf_general_settings_link', 'javascript:void(0)' ), |
| 316 |
'priority' => 7, |
| 317 |
) |
| 318 |
); |
| 319 |
array_push( |
| 320 |
$menu, |
| 321 |
array( |
| 322 |
'title' => __( 'Advanced', 'funnel-builder' ), |
| 323 |
'slug' => 'funnelkit_advanced', |
| 324 |
'link' => apply_filters( 'bwf_general_settings_link', 'javascript:void(0)' ), |
| 325 |
'priority' => 70, |
| 326 |
) |
| 327 |
); |
| 328 |
array_push( |
| 329 |
$menu, |
| 330 |
array( |
| 331 |
'title' => __( 'Notifications', 'funnel-builder' ), |
| 332 |
'slug' => 'funnelkit_notifications', |
| 333 |
'link' => apply_filters( 'bwf_general_settings_link', 'javascript:void(0)' ), |
| 334 |
'priority' => 70, |
| 335 |
) |
| 336 |
); |
| 337 |
|
| 338 |
return $menu; |
| 339 |
} |
| 340 |
|
| 341 |
public function add_settings_fields_array( $settings ) { |
| 342 |
|
| 343 |
$temp_settings = $settings['woofunnels_general_settings']; |
| 344 |
$pixel_tracking = array(); |
| 345 |
$first_party_tracking = array(); |
| 346 |
$funnelkit_advanced = array(); |
| 347 |
$notification_settings = array( |
| 348 |
'title' => 'Email Performance Summary', |
| 349 |
'heading' => 'Email Performance Summary', |
| 350 |
'slug' => 'email_performance_summary', |
| 351 |
'fields' => array( |
| 352 |
array( |
| 353 |
'key' => 'bwf_enable_notification', |
| 354 |
'label' => __( 'Enable Email Performance Summary', 'funnel-builder' ), |
| 355 |
'styleClasses' => 'wffn-tools-toggle bwf-tooglecontrol-advance', |
| 356 |
'type' => 'toggle', |
| 357 |
), |
| 358 |
array( |
| 359 |
'key' => 'bwf_notification_frequency', |
| 360 |
'label' => __( 'Frequency', 'funnel-builder' ), |
| 361 |
'type' => 'checkbox_grid', |
| 362 |
'class' => '', |
| 363 |
'placeholder' => '', |
| 364 |
'required' => false, |
| 365 |
'options' => array( |
| 366 |
'weekly' => __( 'Weekly', 'funnel-builder' ), |
| 367 |
'monthly' => __( 'Monthly', 'funnel-builder' ), |
| 368 |
), |
| 369 |
'hint' => __( 'Emails will be skipped if there are no metrics to show', 'funnel-builder' ), |
| 370 |
'toggler' => array( |
| 371 |
'key' => 'bwf_enable_notification', |
| 372 |
'value' => true, |
| 373 |
), |
| 374 |
), |
| 375 |
array( |
| 376 |
'key' => 'bwf_notification_user_selector', |
| 377 |
'label' => __( 'Users', 'funnel-builder' ), |
| 378 |
'type' => 'search', |
| 379 |
'autocompleter' => 'users', |
| 380 |
'allowFreeTextSearch' => false, |
| 381 |
'required' => false, |
| 382 |
'wrap_before' => '', |
| 383 |
'toggler' => array( |
| 384 |
'key' => 'bwf_enable_notification', |
| 385 |
'value' => true, |
| 386 |
), |
| 387 |
), |
| 388 |
array( |
| 389 |
'key' => 'bwf_external_user', |
| 390 |
'label' => __( 'Other Recipient', 'funnel-builder' ), |
| 391 |
'type' => 'addrecipient', |
| 392 |
'class' => '', |
| 393 |
'required' => false, |
| 394 |
'toggler' => array( |
| 395 |
'key' => 'bwf_enable_notification', |
| 396 |
'value' => true, |
| 397 |
), |
| 398 |
), |
| 399 |
|
| 400 |
array( |
| 401 |
'key' => 'bwf_notification_time', |
| 402 |
'type' => 'timeselector', |
| 403 |
'label' => __( 'Send Time', 'funnel-builder' ), |
| 404 |
'toggler' => array( |
| 405 |
'key' => 'bwf_enable_notification', |
| 406 |
'value' => true, |
| 407 |
), |
| 408 |
), |
| 409 |
array( |
| 410 |
'key' => 'send_test_mail', |
| 411 |
'type' => 'testmail', |
| 412 |
'label' => '', |
| 413 |
'class' => 'bwf-position-test-mail-bottom', |
| 414 |
'toggler' => array( |
| 415 |
'key' => 'bwf_enable_notification', |
| 416 |
'value' => true, |
| 417 |
), |
| 418 |
), |
| 419 |
), |
| 420 |
); |
| 421 |
|
| 422 |
$values = array(); |
| 423 |
foreach ( $notification_settings['fields'] as &$field ) { |
| 424 |
|
| 425 |
$values[ $field['key'] ] = BWF_Admin_General_Settings::get_instance()->get_option( $field['key'] ); |
| 426 |
} |
| 427 |
$notification_settings['values'] = $values; |
| 428 |
|
| 429 |
$filter_setting = array_filter( |
| 430 |
$temp_settings, |
| 431 |
function ( $v, $k ) use ( &$pixel_tracking, &$first_party_tracking, &$funnelkit_advanced ) { |
| 432 |
if ( in_array( $k, array( 'general', 'permalinks', 'fk_stripe_gateway', 'funnelkit_google_maps' ), true ) ) { |
| 433 |
return true; |
| 434 |
} |
| 435 |
if ( 'funnelkit_advanced' === $k ) { |
| 436 |
$funnelkit_advanced[ $k ] = $v; |
| 437 |
} elseif ( 'utm_parameter' === $k ) { |
| 438 |
$first_party_tracking[ $k ] = $v; |
| 439 |
} else { |
| 440 |
$pixel_tracking[ $k ] = $v; |
| 441 |
} |
| 442 |
|
| 443 |
return false; |
| 444 |
}, |
| 445 |
ARRAY_FILTER_USE_BOTH |
| 446 |
); |
| 447 |
|
| 448 |
if ( defined( 'WFFN_PRO_VERSION' ) ) { |
| 449 |
$settings['funnelkit_first_party_tracking'] = array( |
| 450 |
array( |
| 451 |
'tabs' => $first_party_tracking, |
| 452 |
), |
| 453 |
); |
| 454 |
} |
| 455 |
|
| 456 |
$settings['funnelkit_pixel_tracking'] = array( |
| 457 |
array( |
| 458 |
'tabs' => $pixel_tracking, |
| 459 |
), |
| 460 |
); |
| 461 |
$settings['funnelkit_notifications'] = array( |
| 462 |
array( |
| 463 |
'tabs' => array( |
| 464 |
'funnelkit_notifications' => $notification_settings, |
| 465 |
), |
| 466 |
), |
| 467 |
); |
| 468 |
$settings['funnelkit_advanced'] = array( |
| 469 |
array( |
| 470 |
'tabs' => $funnelkit_advanced, |
| 471 |
), |
| 472 |
); |
| 473 |
|
| 474 |
$permalinks = $filter_setting['permalinks']; |
| 475 |
|
| 476 |
/** |
| 477 |
* place upsell permalink after checkout link |
| 478 |
*/ |
| 479 |
if ( isset( $permalinks['fields'] ) && is_array( $permalinks['fields'] ) ) { |
| 480 |
$wfocu_key = array_search( 'wfocu_page_base', wp_list_pluck( $permalinks['fields'], 'key' ), true ); |
| 481 |
if ( false !== $wfocu_key ) { |
| 482 |
$wfacp_key = array_search( 'checkout_page_base', wp_list_pluck( $permalinks['fields'], 'key' ), true ); |
| 483 |
if ( false !== $wfacp_key ) { |
| 484 |
$wfocu_field = $permalinks['fields'][ $wfocu_key ]; |
| 485 |
unset( $permalinks['fields'][ $wfocu_key ] ); |
| 486 |
array_splice( $permalinks['fields'], $wfacp_key + 1, 0, array( $wfocu_field ) ); |
| 487 |
|
| 488 |
} |
| 489 |
} |
| 490 |
} |
| 491 |
|
| 492 |
$settings['woofunnels_general_settings'] = array( |
| 493 |
array( |
| 494 |
'heading' => __( 'License', 'funnel-builder' ), |
| 495 |
'tabs' => array( 'general' => $temp_settings['general'] ), |
| 496 |
), |
| 497 |
array( |
| 498 |
'heading' => __( 'Permalinks', 'funnel-builder' ), |
| 499 |
'tabs' => array( 'permalinks' => $permalinks ), |
| 500 |
), |
| 501 |
|
| 502 |
array( |
| 503 |
'heading' => __( 'Google Maps', 'funnel-builder' ), |
| 504 |
'tabs' => array( 'funnelkit_google_maps' => $temp_settings['funnelkit_google_maps'] ), |
| 505 |
), |
| 506 |
); |
| 507 |
|
| 508 |
return $settings; |
| 509 |
} |
| 510 |
|
| 511 |
public function add_automations_menu() { |
| 512 |
|
| 513 |
if ( class_exists( 'BWFAN_Core' ) ) { |
| 514 |
return; |
| 515 |
} |
| 516 |
$user = WFFN_Role_Capability::get_instance()->user_access( 'menu', 'read' ); |
| 517 |
if ( $user ) { |
| 518 |
add_submenu_page( |
| 519 |
'woofunnels', |
| 520 |
__( 'Automations', 'funnel-builder' ), |
| 521 |
__( 'Automations', 'funnel-builder' ), |
| 522 |
$user, |
| 523 |
'bwf&path=/automations', |
| 524 |
array( |
| 525 |
$this, |
| 526 |
'bwf_funnel_pages', |
| 527 |
) |
| 528 |
); |
| 529 |
} |
| 530 |
} |
| 531 |
|
| 532 |
public function add_subscriptions_menu() { |
| 533 |
$user = WFFN_Role_Capability::get_instance()->user_access( 'menu', 'read' ); |
| 534 |
if ( $user ) { |
| 535 |
add_submenu_page( |
| 536 |
'woofunnels', |
| 537 |
__( 'Subscriptions', 'funnel-builder' ), |
| 538 |
__( 'Subscriptions', 'funnel-builder' ) . '<span style="padding-left: 2px;color: #f18200; vertical-align: super; font-size: 9px;"> NEW!</span>', |
| 539 |
$user, |
| 540 |
'bwf&path=/subscriptions', |
| 541 |
array( |
| 542 |
$this, |
| 543 |
'bwf_funnel_pages', |
| 544 |
) |
| 545 |
); |
| 546 |
} |
| 547 |
} |
| 548 |
|
| 549 |
/** |
| 550 |
* Add update count badge to FunnelKit main menu |
| 551 |
* Hooks into admin_head at priority 9999 to run AFTER core woofunnels menu modifications |
| 552 |
* Core's correct_sub_menu_order() runs at admin_head priority 999 |
| 553 |
* This approach avoids modifying core woofunnels files |
| 554 |
* |
| 555 |
* @hooked admin_head - 9999 |
| 556 |
* @return void |
| 557 |
*/ |
| 558 |
public function add_menu_update_badge() { |
| 559 |
global $menu; |
| 560 |
|
| 561 |
// Get the updates count HTML badge |
| 562 |
$updates_count_html = $this->get_updates_count_html(); |
| 563 |
|
| 564 |
// If no badge to show, return early |
| 565 |
if ( empty( $updates_count_html ) ) { |
| 566 |
return; |
| 567 |
} |
| 568 |
|
| 569 |
// Find and update the FunnelKit menu item |
| 570 |
foreach ( $menu as $key => &$menu_item ) { |
| 571 |
// Check if this is the FunnelKit menu (woofunnels page) |
| 572 |
if ( isset( $menu_item[5] ) && $menu_item[5] === 'toplevel_page_woofunnels' ) { |
| 573 |
// Add the badge to the menu title (index 0) |
| 574 |
$menu_item[0] = $menu_item[0] . $updates_count_html; |
| 575 |
break; |
| 576 |
} |
| 577 |
} |
| 578 |
} |
| 579 |
|
| 580 |
/** |
| 581 |
* Get the number of updates available for FunnelKit |
| 582 |
* Returns 1 when BF or promotional campaigns are active |
| 583 |
* |
| 584 |
* @return int Number of updates available |
| 585 |
*/ |
| 586 |
public function get_updates_count() { |
| 587 |
// Check if any promotional campaign is active |
| 588 |
if ( $this->is_promotional_campaign_active() ) { |
| 589 |
return 1; |
| 590 |
} |
| 591 |
|
| 592 |
// No active campaigns |
| 593 |
return 0; |
| 594 |
} |
| 595 |
|
| 596 |
/** |
| 597 |
* Check if any promotional campaign is active and NOT dismissed |
| 598 |
* Follows the complete user flow: Sticky Banner → Transient Cooldown → Admin Notification |
| 599 |
* |
| 600 |
* Badge shows if: |
| 601 |
* - Campaign is active AND |
| 602 |
* - Transient cooldown is NOT active AND |
| 603 |
* - Either sticky banner OR admin notification is NOT dismissed |
| 604 |
* |
| 605 |
* User Journey: |
| 606 |
* 1. Sticky shows (not dismissed) → Badge shows |
| 607 |
* 2. User dismisses sticky → Transient active (5 min) → Badge hidden |
| 608 |
* 3. Transient expires → Notification shows → Badge shows |
| 609 |
* 4. User dismisses notification → Badge hidden permanently |
| 610 |
* |
| 611 |
* Total 7 campaigns grouped into 3 menu items: |
| 612 |
* - BF menu: Pre-BF, BF, Small Business Saturday, BF Extended |
| 613 |
* - CM menu: CM, Extended CM |
| 614 |
* - Green Monday menu: GM |
| 615 |
* |
| 616 |
* @return bool True if campaign active and not fully dismissed, false otherwise |
| 617 |
*/ |
| 618 |
public function is_promotional_campaign_active() { |
| 619 |
// Only show for non-PRO users |
| 620 |
if ( defined( 'WFFN_PRO_VERSION' ) ) { |
| 621 |
return false; |
| 622 |
} |
| 623 |
|
| 624 |
$year = gmdate( 'Y' ); |
| 625 |
$current_user_id = get_current_user_id(); |
| 626 |
$notifications = WFFN_Core()->admin_notifications; |
| 627 |
|
| 628 |
// Check if transient cooldown is active (5-minute period) - if yes, don't show badge |
| 629 |
$transient = get_transient( 'wffn_sticky_banner_dismissed_' . $current_user_id ); |
| 630 |
if ( false !== $transient ) { |
| 631 |
return false; |
| 632 |
} |
| 633 |
|
| 634 |
// BF group campaigns (4 campaigns) |
| 635 |
if ( $notifications->show_pre_bfcm_header_notification() ) { |
| 636 |
$notification_key = 'promo_bf_pre_' . $year; |
| 637 |
$sticky_key = 'sticky_pre_bfcm_' . $year; |
| 638 |
// Show badge if sticky is active (not dismissed) OR if notification is active (not dismissed) |
| 639 |
if ( ! $notifications->is_user_dismissed( $current_user_id, $sticky_key ) |
| 640 |
|| ! $notifications->is_user_dismissed( $current_user_id, $notification_key ) ) { |
| 641 |
return true; |
| 642 |
} |
| 643 |
} |
| 644 |
|
| 645 |
if ( $notifications->show_bf_header_notification() ) { |
| 646 |
$notification_key = 'promo_bf_' . $year; |
| 647 |
$sticky_key = 'sticky_bfcm_' . $year; |
| 648 |
|
| 649 |
if ( ! $notifications->is_user_dismissed( $current_user_id, $sticky_key ) |
| 650 |
|| ! $notifications->is_user_dismissed( $current_user_id, $notification_key ) ) { |
| 651 |
return true; |
| 652 |
} |
| 653 |
} |
| 654 |
|
| 655 |
if ( $notifications->show_small_business_saturday_header_notification() ) { |
| 656 |
$notification_key = 'promo_bf_sbs_' . $year; |
| 657 |
$sticky_key = 'sticky_sbs_' . $year; |
| 658 |
|
| 659 |
if ( ! $notifications->is_user_dismissed( $current_user_id, $sticky_key ) |
| 660 |
|| ! $notifications->is_user_dismissed( $current_user_id, $notification_key ) ) { |
| 661 |
return true; |
| 662 |
} |
| 663 |
} |
| 664 |
|
| 665 |
if ( $notifications->show_bfext_header_notification() ) { |
| 666 |
$notification_key = 'promo_bf_bfext_' . $year; |
| 667 |
$sticky_key = 'sticky_bfext_' . $year; |
| 668 |
|
| 669 |
if ( ! $notifications->is_user_dismissed( $current_user_id, $sticky_key ) |
| 670 |
|| ! $notifications->is_user_dismissed( $current_user_id, $notification_key ) ) { |
| 671 |
return true; |
| 672 |
} |
| 673 |
} |
| 674 |
|
| 675 |
// CM group campaigns (2 campaigns) |
| 676 |
if ( $notifications->show_cm_header_notification() ) { |
| 677 |
$notification_key = 'promo_bf_cm_' . $year; |
| 678 |
$sticky_key = 'sticky_cm_' . $year; |
| 679 |
|
| 680 |
if ( ! $notifications->is_user_dismissed( $current_user_id, $sticky_key ) |
| 681 |
|| ! $notifications->is_user_dismissed( $current_user_id, $notification_key ) ) { |
| 682 |
return true; |
| 683 |
} |
| 684 |
} |
| 685 |
|
| 686 |
if ( $notifications->show_cmext_header_notification() ) { |
| 687 |
$notification_key = 'promo_bf_cmext_' . $year; |
| 688 |
$sticky_key = 'sticky_cmext_' . $year; |
| 689 |
|
| 690 |
if ( ! $notifications->is_user_dismissed( $current_user_id, $sticky_key ) |
| 691 |
|| ! $notifications->is_user_dismissed( $current_user_id, $notification_key ) ) { |
| 692 |
return true; |
| 693 |
} |
| 694 |
} |
| 695 |
|
| 696 |
// Green Monday campaign (1 campaign) |
| 697 |
if ( $notifications->show_green_monday_header_notification() ) { |
| 698 |
$notification_key = 'promo_bf_gm_' . $year; |
| 699 |
$sticky_key = 'sticky_gm_' . $year; |
| 700 |
|
| 701 |
if ( ! $notifications->is_user_dismissed( $current_user_id, $sticky_key ) |
| 702 |
|| ! $notifications->is_user_dismissed( $current_user_id, $notification_key ) ) { |
| 703 |
return true; |
| 704 |
} |
| 705 |
} |
| 706 |
|
| 707 |
return false; |
| 708 |
} |
| 709 |
|
| 710 |
/** |
| 711 |
* Return the updates count markup for FunnelKit menus |
| 712 |
* |
| 713 |
* @return string Updates count markup, empty string if no updates available |
| 714 |
*/ |
| 715 |
public function get_updates_count_html() { |
| 716 |
$count = $this->get_updates_count(); |
| 717 |
|
| 718 |
if ( $count <= 0 ) { |
| 719 |
return ''; |
| 720 |
} |
| 721 |
|
| 722 |
$count_html = sprintf( |
| 723 |
' <span class="update-plugins count-%d"><span class="update-count">%d</span></span>', |
| 724 |
$count, |
| 725 |
number_format_i18n( $count ) |
| 726 |
); |
| 727 |
|
| 728 |
return $count_html; |
| 729 |
} |
| 730 |
|
| 731 |
public function register_admin_menu() { |
| 732 |
$steps = WFFN_Core()->steps->get_supported_steps(); |
| 733 |
if ( count( $steps ) < 1 ) { |
| 734 |
return; |
| 735 |
} |
| 736 |
|
| 737 |
$user = WFFN_Role_Capability::get_instance()->user_access( 'menu', 'read' ); |
| 738 |
if ( $user ) { |
| 739 |
|
| 740 |
add_submenu_page( |
| 741 |
'woofunnels', |
| 742 |
__( 'Dashboard', 'funnel-builder' ), |
| 743 |
__( 'Dashboard', 'funnel-builder' ), |
| 744 |
$user, |
| 745 |
'bwf', |
| 746 |
array( |
| 747 |
$this, |
| 748 |
'bwf_funnel_pages', |
| 749 |
) |
| 750 |
); |
| 751 |
|
| 752 |
add_submenu_page( |
| 753 |
'woofunnels', |
| 754 |
__( 'Funnels', 'funnel-builder' ), |
| 755 |
__( 'Funnels', 'funnel-builder' ), |
| 756 |
$user, |
| 757 |
'bwf&path=/funnels', |
| 758 |
array( |
| 759 |
$this, |
| 760 |
'bwf_funnel_pages', |
| 761 |
) |
| 762 |
); |
| 763 |
|
| 764 |
add_submenu_page( |
| 765 |
'woofunnels', |
| 766 |
__( 'Templates', 'funnel-builder' ), |
| 767 |
__( 'Templates', 'funnel-builder' ), |
| 768 |
$user, |
| 769 |
'bwf&path=/templates', |
| 770 |
array( |
| 771 |
$this, |
| 772 |
'bwf_funnel_pages', |
| 773 |
) |
| 774 |
); |
| 775 |
add_submenu_page( |
| 776 |
'woofunnels', |
| 777 |
__( 'Analytics', 'funnel-builder' ), |
| 778 |
__( 'Analytics', 'funnel-builder' ), |
| 779 |
$user, |
| 780 |
'bwf&path=/analytics', |
| 781 |
array( |
| 782 |
$this, |
| 783 |
'bwf_funnel_pages', |
| 784 |
) |
| 785 |
); |
| 786 |
|
| 787 |
add_submenu_page( |
| 788 |
'woofunnels', |
| 789 |
__( 'Store Checkout', 'funnel-builder' ), |
| 790 |
__( 'Store Checkout', 'funnel-builder' ), |
| 791 |
$user, |
| 792 |
'bwf&path=/store-checkout', |
| 793 |
array( |
| 794 |
$this, |
| 795 |
'bwf_funnel_pages', |
| 796 |
) |
| 797 |
); |
| 798 |
} |
| 799 |
} |
| 800 |
|
| 801 |
public function is_basic_exists() { |
| 802 |
return defined( 'WFFN_BASIC_FILE' ); |
| 803 |
} |
| 804 |
|
| 805 |
public function bwf_funnel_pages() { |
| 806 |
|
| 807 |
?> |
| 808 |
<div id="wffn-contacts" class="wffn-page"> |
| 809 |
</div> |
| 810 |
<?php |
| 811 |
|
| 812 |
wp_enqueue_style( 'wffn-flex-admin', $this->get_admin_url() . '/assets/css/admin.css', array(), WFFN_VERSION_DEV ); |
| 813 |
} |
| 814 |
|
| 815 |
public function admin_enqueue_assets( $hook_suffix ) { |
| 816 |
wp_enqueue_style( 'bwf-admin-font', $this->get_admin_url() . '/assets/css/bwf-admin-font.css', array(), WFFN_VERSION_DEV ); |
| 817 |
|
| 818 |
if ( strpos( $hook_suffix, 'woofunnels_page' ) > - 1 || strpos( $hook_suffix, 'page_woofunnels' ) > - 1 ) { |
| 819 |
wp_enqueue_style( 'bwf-admin-header', $this->get_admin_url() . '/assets/css/admin-global-header.css', array(), WFFN_VERSION_DEV ); |
| 820 |
} |
| 821 |
|
| 822 |
if ( $this->is_wffn_flex_page( 'all' ) ) { |
| 823 |
if ( WFFN_Role_Capability::get_instance()->user_access( 'funnel', 'write' ) ) { |
| 824 |
add_filter( 'user_can_richedit', '__return_true', 999 ); |
| 825 |
} |
| 826 |
|
| 827 |
wp_enqueue_style( 'wffn-flex-admin', $this->get_admin_url() . '/assets/css/admin.css', array(), WFFN_VERSION_DEV ); |
| 828 |
|
| 829 |
if ( WFFN_Core()->admin->is_wffn_flex_page() ) { |
| 830 |
$this->load_react_app( 'main-20260904101952' ); //phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation |
| 831 |
if ( isset( $_GET['page'] ) && $_GET['page'] === 'bwf' && method_exists( 'BWF_Admin_General_Settings', 'get_localized_bwf_data' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 832 |
wp_localize_script( 'wffn-contact-admin', 'bwfAdminGen', BWF_Admin_General_Settings::get_instance()->get_localized_bwf_data() ); |
| 833 |
|
| 834 |
} |
| 835 |
|
| 836 |
add_filter( |
| 837 |
'wffn_noconflict_scripts', |
| 838 |
function ( $scripts = array() ) { |
| 839 |
return array_merge( $scripts, array( 'wffn-contact-admin' ) ); |
| 840 |
} |
| 841 |
); |
| 842 |
} |
| 843 |
|
| 844 |
do_action( 'wffn_admin_assets', $this ); |
| 845 |
} |
| 846 |
} |
| 847 |
|
| 848 |
public function get_local_app_path() { |
| 849 |
return '/admin/app/dist/'; |
| 850 |
} |
| 851 |
|
| 852 |
public function get_editor_priority_theme_name() { |
| 853 |
try { |
| 854 |
$current_theme = strtolower( get_template() ); |
| 855 |
|
| 856 |
// Theme priority mapping |
| 857 |
$theme_map = array( |
| 858 |
'flatsome' => 'Flatsome', |
| 859 |
'avada' => 'Avada', |
| 860 |
'x' => 'X', |
| 861 |
); |
| 862 |
|
| 863 |
// Check for theme priority first |
| 864 |
if ( isset( $theme_map[ $current_theme ] ) ) { |
| 865 |
return $theme_map[ $current_theme ]; |
| 866 |
} |
| 867 |
|
| 868 |
// Plugin priority mapping (page builders) |
| 869 |
$plugin_map = array( |
| 870 |
'breakdance/plugin.php' => 'Breakdance', |
| 871 |
'thrive-visual-editor/thrive-visual-editor.php' => 'Thrive', |
| 872 |
'bb-plugin/fl-builder.php' => 'Beaver Builder', |
| 873 |
'js_composer/js_composer.php' => 'Visual Composer', |
| 874 |
'visualcomposer/visualcomposer.php' => 'Visual Composer', |
| 875 |
'themify-builder/themify-builder.php' => 'Themify Builder', |
| 876 |
'brizy/brizy.php' => 'Brizy', |
| 877 |
'zionbuilder/zionbuilder.php' => 'Zion', |
| 878 |
'seedprod/seedprod.php' => 'SeedProd', |
| 879 |
); |
| 880 |
|
| 881 |
// Check for plugin priority |
| 882 |
foreach ( $plugin_map as $plugin_basename => $plugin_name ) { |
| 883 |
if ( wffn_is_plugin_active( $plugin_basename ) ) { |
| 884 |
return $plugin_name; |
| 885 |
} |
| 886 |
} |
| 887 |
|
| 888 |
return ''; |
| 889 |
} catch ( \Throwable $e ) { |
| 890 |
// Log error and return empty string |
| 891 |
BWF_logger::get_instance()->log( 'Error in get_editor_priority_theme_name: ' . $e->getMessage(), 'wffn_admin' ); |
| 892 |
return ''; |
| 893 |
} |
| 894 |
} |
| 895 |
|
| 896 |
|
| 897 |
|
| 898 |
public function load_react_app( $app_name = 'main' ) { |
| 899 |
$app_name = str_replace( '-{{{APP_VERSION}}}', '', $app_name ); //phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation |
| 900 |
$min = 60 * get_option( 'gmt_offset' ); |
| 901 |
$sign = $min < 0 ? '-' : '+'; |
| 902 |
$absmin = abs( $min ); |
| 903 |
$tz = sprintf( '%s%02d:%02d', $sign, $absmin / 60, $absmin % 60 ); |
| 904 |
$contact_page_data = array( |
| 905 |
'is_wc_active' => false, |
| 906 |
'is_wc_square_active' => WFFN_Common::is_wc_square_active(), |
| 907 |
'date_format' => get_option( 'date_format', 'F j, Y' ), |
| 908 |
'time_format' => get_option( 'time_format', 'g:i a' ), |
| 909 |
'app_path' => WFFN_Core()->get_plugin_url() . '/admin/app/dist/', |
| 910 |
'timezone' => $tz, |
| 911 |
'flag_img' => WFFN_Core()->get_plugin_url() . '/admin/assets/img/phone/flags.png', |
| 912 |
'updated_pro_version' => defined( 'WFFN_PRO_VERSION' ) && version_compare( WFFN_PRO_VERSION, '3.0.0 beta', '>=' ), |
| 913 |
'activation_date' => array( |
| 914 |
'lite' => $this->get_lite_activation_date(), |
| 915 |
'pro' => $this->get_pro_activation_date(), |
| 916 |
), |
| 917 |
'get_pro_link' => WFFN_Core()->admin->get_pro_link(), |
| 918 |
'wc_add_product_url' => admin_url( 'post-new.php?post_type=product' ), |
| 919 |
'admin_url' => admin_url(), |
| 920 |
'multilingual' => $this->is_language_support_enabled(), |
| 921 |
'multilingual_name' => WFFN_Plugin_Compatibilities::get_language_compatible_plugin(), |
| 922 |
'block_editor_priority_theme' => $this->get_editor_priority_theme_name(), |
| 923 |
); |
| 924 |
if ( class_exists( 'WooCommerce' ) ) { |
| 925 |
$currency = get_woocommerce_currency(); |
| 926 |
$contact_page_data['currency'] = array( |
| 927 |
'code' => $currency, |
| 928 |
'precision' => wc_get_price_decimals(), |
| 929 |
'symbol' => html_entity_decode( get_woocommerce_currency_symbol( $currency ), ENT_QUOTES | ENT_HTML401 ), |
| 930 |
'symbolPosition' => get_option( 'woocommerce_currency_pos' ), |
| 931 |
'decimalSeparator' => wc_get_price_decimal_separator(), |
| 932 |
'thousandSeparator' => wc_get_price_thousand_separator(), |
| 933 |
'priceFormat' => html_entity_decode( get_woocommerce_price_format(), ENT_QUOTES | ENT_HTML401 ), |
| 934 |
); |
| 935 |
$contact_page_data['is_wc_active'] = true; |
| 936 |
} |
| 937 |
|
| 938 |
$frontend_dir = ( 0 === WFFN_REACT_ENVIRONMENT ) ? WFFN_REACT_DEV_URL : WFFN_Core()->get_plugin_url() . $this->get_local_app_path(); |
| 939 |
if ( class_exists( 'WooCommerce' ) ) { |
| 940 |
wp_dequeue_style( 'woocommerce_admin_styles' ); |
| 941 |
wp_dequeue_style( 'wc-components' ); |
| 942 |
} |
| 943 |
|
| 944 |
$assets_path = 1 === WFFN_REACT_ENVIRONMENT ? WFFN_PLUGIN_DIR . $this->get_local_app_path() . "$app_name.asset.php" : $frontend_dir . "/$app_name.asset.php"; |
| 945 |
$assets = file_exists( $assets_path ) ? include $assets_path : array( |
| 946 |
'dependencies' => array( |
| 947 |
'lodash', |
| 948 |
'moment', |
| 949 |
'react', |
| 950 |
'react-dom', |
| 951 |
'wp-api-fetch', |
| 952 |
'wp-components', |
| 953 |
'wp-compose', |
| 954 |
'wp-date', |
| 955 |
'wp-deprecated', |
| 956 |
'wp-block-editor', |
| 957 |
'wp-block-library', |
| 958 |
'wp-dom', |
| 959 |
'wp-element', |
| 960 |
'wp-hooks', |
| 961 |
'wp-html-entities', |
| 962 |
'wp-i18n', |
| 963 |
'wp-keycodes', |
| 964 |
'wp-polyfill', |
| 965 |
'wp-primitives', |
| 966 |
'wp-url', |
| 967 |
'wp-viewport', |
| 968 |
'wp-color-picker', |
| 969 |
'wp-i18n', |
| 970 |
), |
| 971 |
'version' => time(), |
| 972 |
); |
| 973 |
$deps = ( isset( $assets['dependencies'] ) ? array_merge( $assets['dependencies'], array( 'jquery' ) ) : array( 'jquery' ) ); |
| 974 |
$version = $assets['version']; |
| 975 |
|
| 976 |
$script_deps = array_filter( |
| 977 |
$deps, |
| 978 |
function ( $dep ) { |
| 979 |
return false === strpos( $dep, 'css' ); |
| 980 |
} |
| 981 |
); |
| 982 |
if ( 'settings' === $app_name ) { |
| 983 |
$script_deps = array_merge( $script_deps, array( 'wp-color-picker' ) ); |
| 984 |
} |
| 985 |
|
| 986 |
if ( class_exists( 'WFFN_Header' ) ) { |
| 987 |
$header_ins = new WFFN_Header(); |
| 988 |
$contact_page_data['header_data'] = $header_ins->get_render_data(); |
| 989 |
} |
| 990 |
|
| 991 |
$contact_page_data['localize_texts'] = apply_filters( 'wffn_localized_text_admin', array() ); |
| 992 |
|
| 993 |
wp_enqueue_style( 'wp-components' ); |
| 994 |
wp_enqueue_style( 'wffn-contact-admin', $frontend_dir . "$app_name.css", array(), $version ); |
| 995 |
wp_register_script( 'wffn-contact-admin', $frontend_dir . "$app_name.js", $script_deps, $version, true ); |
| 996 |
wp_localize_script( 'wffn-contact-admin', 'wffn_contacts_data', $contact_page_data ); |
| 997 |
wp_enqueue_script( 'wffn-contact-admin' ); |
| 998 |
wp_set_script_translations( 'wffn-contact-admin', 'funnel-builder' ); |
| 999 |
|
| 1000 |
wp_enqueue_editor(); |
| 1001 |
wp_tinymce_inline_scripts(); |
| 1002 |
wp_enqueue_media(); |
| 1003 |
} |
| 1004 |
|
| 1005 |
|
| 1006 |
public function get_admin_url() { |
| 1007 |
return WFFN_Core()->get_plugin_url() . '/admin'; |
| 1008 |
} |
| 1009 |
|
| 1010 |
public function get_admin_path() { |
| 1011 |
return WFFN_PLUGIN_DIR . '/admin'; |
| 1012 |
} |
| 1013 |
|
| 1014 |
/** |
| 1015 |
* @param string $page |
| 1016 |
* |
| 1017 |
* @return bool |
| 1018 |
*/ |
| 1019 |
public function is_wffn_flex_page( $page = 'bwf' ) { |
| 1020 |
|
| 1021 |
if ( isset( $_GET['page'] ) && $_GET['page'] === $page ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 1022 |
return true; |
| 1023 |
} |
| 1024 |
|
| 1025 |
if ( isset( $_GET['page'] ) && 'bwf' === $_GET['page'] && 'all' === $page ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 1026 |
return true; |
| 1027 |
} |
| 1028 |
|
| 1029 |
return false; |
| 1030 |
} |
| 1031 |
|
| 1032 |
|
| 1033 |
public function js_variables() { |
| 1034 |
if ( $this->is_wffn_flex_page( 'all' ) ) { |
| 1035 |
$steps_data = WFFN_Common::get_steps_data(); |
| 1036 |
$substeps_data = WFFN_Common::get_substeps_data(); |
| 1037 |
$substeps_data['substep'] = true; |
| 1038 |
|
| 1039 |
$upsell_exist = function_exists( 'WFOCU_Core' ); |
| 1040 |
|
| 1041 |
$data = array( |
| 1042 |
'steps_data' => $steps_data, |
| 1043 |
'substeps' => $substeps_data, |
| 1044 |
'icons' => array( |
| 1045 |
'error_cross' => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 130.2 130.2" class="wffn_loader wffn_loader_error"> |
| 1046 |
<circle fill="#e6283f" stroke="#e6283f" stroke-width="6" stroke-miterlimit="10" cx="65.1" cy="65.1" r="62.1" class="path circle"></circle> |
| 1047 |
<line fill="none" stroke="#ffffff" stroke-width="8" stroke-linecap="round" stroke-miterlimit="10" x1="34.4" y1="37.9" x2="95.8" y2="92.3" class="path line"></line> |
| 1048 |
<line fill="none" stroke="#ffffff" stroke-width="8" stroke-linecap="round" stroke-miterlimit="10" x1="95.8" y1="38" x2="34.4" y2="92.2" class="path line"></line> |
| 1049 |
</svg>', |
| 1050 |
'success_check' => '<svg class="wffn_loader wffn_loader_ok" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 130.2 130.2"> |
| 1051 |
<circle class="path circle" fill="#13c37b" stroke="#13c37b" stroke-width="5" stroke-miterlimit="10" cx="65.1" cy="65.1" r="62.1"></circle> |
| 1052 |
<polyline class="path check" fill="none" stroke="#ffffff" stroke-width="9" stroke-linecap="round" stroke-miterlimit="10" points="100.2,40.2 51.5,88.8 29.8,67.5 "></polyline> |
| 1053 |
</svg>', |
| 1054 |
'delete_alert' => '<div class="swal2-header wf_funnel-icon-without-swal"><div class="swal2-icon swal2-warning swal2-animate-warning-icon" style="display: flex;"><span class="swal2-icon-text">!</span></div></div>', |
| 1055 |
), |
| 1056 |
); |
| 1057 |
|
| 1058 |
$data['settings_texts'] = apply_filters( 'wffn_funnel_settings', array() ); |
| 1059 |
|
| 1060 |
$data['i18n'] = array( |
| 1061 |
'plugin_activate' => __( 'Activating plugin...', 'funnel-builder' ), |
| 1062 |
'plugin_install' => __( 'Installing plugin...', 'funnel-builder' ), |
| 1063 |
'preparingsteps' => __( 'Preparing steps...', 'funnel-builder' ), |
| 1064 |
'redirecting' => __( 'Redirecting...', 'funnel-builder' ), |
| 1065 |
'importing' => __( 'Importing...', 'funnel-builder' ), |
| 1066 |
'custom_import' => __( 'Setting up your funnel...', 'funnel-builder' ), |
| 1067 |
'ribbons' => array( |
| 1068 |
'lite' => __( 'Lite', 'funnel-builder' ), |
| 1069 |
'pro' => __( 'PRO', 'funnel-builder' ), |
| 1070 |
), |
| 1071 |
'test' => __( 'Test', 'funnel-builder' ), |
| 1072 |
); |
| 1073 |
if ( wffn_is_wc_active() && false === $upsell_exist ) { |
| 1074 |
$data['wc_upsells'] = array( |
| 1075 |
'type' => 'wc_upsells', |
| 1076 |
'group' => WFFN_Steps::STEP_GROUP_WC, |
| 1077 |
'title' => __( 'One Click Upsells', 'funnel-builder' ), |
| 1078 |
'desc' => __( 'Deploy post purchase one click upsells to increase average order value', 'funnel-builder' ), |
| 1079 |
'dashicons' => 'dashicons-tag', |
| 1080 |
'icon' => 'tags', |
| 1081 |
'pro' => true, |
| 1082 |
); |
| 1083 |
} |
| 1084 |
|
| 1085 |
$data['welcome_note_dismiss'] = get_user_meta( get_current_user_id(), '_wffn_welcome_note_dismissed', true );//phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_get_user_meta |
| 1086 |
$data['is_bump_dismissed'] = get_user_meta( get_current_user_id(), '_wffn_bump_promotion_hide', true );//phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_get_user_meta |
| 1087 |
$data['is_upsell_dismissed'] = get_user_meta( get_current_user_id(), '_wffn_upsell_promotion_hide', true );//phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_get_user_meta |
| 1088 |
|
| 1089 |
$data['current_logged_user'] = get_current_user_id(); |
| 1090 |
$data['is_rtl'] = is_rtl(); |
| 1091 |
|
| 1092 |
$data['is_ab_experiment'] = class_exists( 'BWFABT_Core' ) ? 1 : 0; |
| 1093 |
$data['is_ab_experiment_support'] = ( class_exists( 'BWFABT_Core' ) && version_compare( BWFABT_VERSION, '1.3.5', '>' ) ) ? 1 : 0; |
| 1094 |
$data['wizard_status'] = $this->is_wizard_available(); |
| 1095 |
|
| 1096 |
$data['automation_plugin_status'] = WFFN_Common::get_plugin_status( 'wp-marketing-automations/wp-marketing-automations.php' ); |
| 1097 |
$data['fkcart_img_url'] = esc_url( plugin_dir_url( WFFN_PLUGIN_FILE ) . 'admin/assets/img/fkcart-img.png' ); |
| 1098 |
$data['fkcart_plugin_status'] = WFFN_Common::get_plugin_status( 'cart-for-woocommerce/plugin.php' ); |
| 1099 |
$data['sublium_plugin_status'] = WFFN_Common::get_plugin_status( 'sublium-subscriptions-for-woocommerce/sublium-subscriptions-for-woocommerce.php' ); |
| 1100 |
$data['fkcart_show_analytics'] = ( defined( 'FKCART_DB_VERSION' ) && version_compare( FKCART_DB_VERSION, '1.7.2', '>=' ) ) ? 'yes' : 'no'; |
| 1101 |
$data['ob_arrow_blink_img_url'] = esc_url( plugin_dir_url( WFFN_PLUGIN_FILE ) . 'admin/assets/img/arrow-blink.gif' ); |
| 1102 |
$data['pro_modal_img_path'] = esc_url( plugin_dir_url( WFFN_PLUGIN_FILE ) . 'admin/assets/img/pro_modal/' ); |
| 1103 |
$data['admin_img_path'] = esc_url( plugin_dir_url( WFFN_PLUGIN_FILE ) . 'admin/assets/img/' ); |
| 1104 |
$bwf_notifications = get_user_meta( get_current_user_id(), '_bwf_notifications_close', true ); //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_get_user_meta |
| 1105 |
$bwf_notifications = is_array( $bwf_notifications ) ? array_values( $bwf_notifications ) : $bwf_notifications; |
| 1106 |
$data['user_preferences'] = array( 'notices_close' => $bwf_notifications ); |
| 1107 |
$data['user_has_notifications'] = WFFN_Core()->admin_notifications->user_has_notifications( get_current_user_id() ); |
| 1108 |
$data['pro_link'] = $this->get_pro_link(); |
| 1109 |
$data['upgrade_button_text'] = __( 'Upgrade to PRO Now', 'funnel-builder' ); |
| 1110 |
$data['site_options'] = get_option( 'fb_site_options', array() ); |
| 1111 |
$data['nonce_contact_export_download'] = wp_create_nonce( 'bwf_contact_export_download' ); |
| 1112 |
$data['user_display_name'] = get_user_by( 'id', get_current_user_id() )->display_name; |
| 1113 |
$data['user_email'] = get_user_by( 'id', get_current_user_id() )->user_email; |
| 1114 |
$data['bfcm_promo_sticky_banner'] = WFFN_Core()->admin_notifications->get_sticky_banner(); |
| 1115 |
$data['bfcm_promo_sticky_secondary_banner'] = WFFN_Core()->admin_notifications->get_sticky_secondary_banner(); |
| 1116 |
|
| 1117 |
$data['pro_version_number'] = defined( 'WFFN_PRO_VERSION' ) ? WFFN_PRO_VERSION : '0.0.0'; |
| 1118 |
$data['cart_analytics_version'] = '3.11.0'; |
| 1119 |
$data['conditional_fields_version'] = '3.15.0'; |
| 1120 |
$data['site_domain'] = $this->get_site_domain_for_admin(); |
| 1121 |
|
| 1122 |
$data['review_count'] = WFFN_REVIEW_RATING_COUNT; |
| 1123 |
$data['feature_enabled'] = array( |
| 1124 |
'funnel_categories' => defined( 'WFFN_PRO_VERSION' ) && version_compare( WFFN_PRO_VERSION, '3.11.0', '>=' ), |
| 1125 |
'conditional_fields' => defined( 'WFFN_PRO_VERSION' ) && version_compare( WFFN_PRO_VERSION, $data['conditional_fields_version'], '>=' ), |
| 1126 |
); |
| 1127 |
$data['fkcart_settings'] = $this->get_fkcart_settings(); |
| 1128 |
|
| 1129 |
?> |
| 1130 |
<script>window.wffn = <?php echo wp_json_encode( apply_filters( 'wffn_localize_admin', $data ) ); ?>;</script> |
| 1131 |
<script> |
| 1132 |
<?php |
| 1133 |
echo ' |
| 1134 |
(function() { |
| 1135 |
setTimeout(() => { |
| 1136 |
const scriptElement = document.getElementById("wffn-contact-admin-js"); |
| 1137 |
if (scriptElement) { |
| 1138 |
|
| 1139 |
scriptElement.onerror = function() { |
| 1140 |
if (typeof window.wffn_loaded === "undefined") { |
| 1141 |
console.warn("Main JS not loaded, retrying with version parameter..."); |
| 1142 |
|
| 1143 |
const newScript = document.createElement("script"); |
| 1144 |
const version = `?ver=${new Date().getTime()}`; |
| 1145 |
newScript.src = `${scriptElement.src.split("?")[0]}${version}`; |
| 1146 |
newScript.id = "wffn-contact-admin-js"; |
| 1147 |
newScript.async = true; |
| 1148 |
|
| 1149 |
document.body.appendChild(newScript); |
| 1150 |
} |
| 1151 |
}; |
| 1152 |
} |
| 1153 |
}, 3000) |
| 1154 |
})(); |
| 1155 |
' |
| 1156 |
?> |
| 1157 |
</script> |
| 1158 |
<?php |
| 1159 |
} |
| 1160 |
} |
| 1161 |
|
| 1162 |
/** |
| 1163 |
* Get the domain that should be localized for the React admin app. |
| 1164 |
* |
| 1165 |
* @return string |
| 1166 |
*/ |
| 1167 |
private function get_site_domain_for_admin() { |
| 1168 |
global $sitepress; |
| 1169 |
|
| 1170 |
$domain = site_url(); |
| 1171 |
|
| 1172 |
if ( isset( $sitepress ) && ! is_null( $sitepress ) ) { |
| 1173 |
$default_language = $sitepress->get_default_language(); |
| 1174 |
$domain = $sitepress->convert_url( $sitepress->get_wp_api()->get_home_url(), $default_language ); |
| 1175 |
} |
| 1176 |
|
| 1177 |
if ( function_exists( 'pll_default_language' ) && function_exists( 'pll_home_url' ) ) { |
| 1178 |
$default_language = pll_default_language(); |
| 1179 |
$domain = pll_home_url( $default_language ); |
| 1180 |
} |
| 1181 |
|
| 1182 |
if ( is_multisite() && $this->is_funnel_builder_network_active() ) { |
| 1183 |
$domain = get_site_url( get_network()->site_id ); |
| 1184 |
} |
| 1185 |
|
| 1186 |
$domain = str_replace( array( 'https://', 'http://' ), '', $domain ); |
| 1187 |
$domain = trim( $domain, '/' ); |
| 1188 |
|
| 1189 |
return $domain; |
| 1190 |
} |
| 1191 |
|
| 1192 |
private function is_funnel_builder_network_active() { |
| 1193 |
$active_plugins = get_site_option( 'active_sitewide_plugins', array() ); |
| 1194 |
$active_plugins = apply_filters( 'active_plugins', $active_plugins ); |
| 1195 |
|
| 1196 |
return is_array( $active_plugins ) && ( in_array( WFFN_PLUGIN_BASENAME, $active_plugins, true ) || array_key_exists( WFFN_PLUGIN_BASENAME, $active_plugins ) ); |
| 1197 |
} |
| 1198 |
|
| 1199 |
/** |
| 1200 |
* Get Cart for WooCommerce plugin settings for admin localization. |
| 1201 |
* Returns enabled, rewards, and upsells status from the Cart plugin when active. |
| 1202 |
* |
| 1203 |
* @return array{enabled: bool, rewards: bool, upsells: bool} |
| 1204 |
*/ |
| 1205 |
public function get_fkcart_settings() { |
| 1206 |
$default = array( |
| 1207 |
'enabled' => false, |
| 1208 |
'rewards' => false, |
| 1209 |
'upsells' => false, |
| 1210 |
); |
| 1211 |
|
| 1212 |
if ( ! class_exists( 'FKCart\Includes\Data' ) ) { |
| 1213 |
return apply_filters( 'wffn_fkcart_settings', $default ); |
| 1214 |
} |
| 1215 |
|
| 1216 |
$settings = array( |
| 1217 |
'enabled' => \FKCart\Includes\Data::is_cart_enabled(), |
| 1218 |
'rewards' => \FKCart\Includes\Data::is_rewards_enabled(), |
| 1219 |
'upsells' => \FKCart\Includes\Data::is_upsells_enabled(), |
| 1220 |
); |
| 1221 |
|
| 1222 |
return apply_filters( 'wffn_fkcart_settings', $settings ); |
| 1223 |
} |
| 1224 |
|
| 1225 |
/** |
| 1226 |
* Get the already setup funnel object |
| 1227 |
* |
| 1228 |
* @return WFFN_Funnel |
| 1229 |
*/ |
| 1230 |
public function get_funnel( $funnel_id = 0 ) { |
| 1231 |
if ( $funnel_id > 0 ) { |
| 1232 |
if ( $this->funnel instanceof WFFN_Funnel && $funnel_id === $this->funnel->get_id() ) { |
| 1233 |
return $this->funnel; |
| 1234 |
} |
| 1235 |
$this->initiate_funnel( $funnel_id ); |
| 1236 |
} |
| 1237 |
if ( $this->funnel instanceof WFFN_Funnel ) { |
| 1238 |
return $this->funnel; |
| 1239 |
} |
| 1240 |
$this->funnel = new WFFN_Funnel( $funnel_id ); |
| 1241 |
|
| 1242 |
return $this->funnel; |
| 1243 |
} |
| 1244 |
|
| 1245 |
/** |
| 1246 |
* @param $funnel_id |
| 1247 |
*/ |
| 1248 |
public function initiate_funnel( $funnel_id ) { |
| 1249 |
if ( ! empty( $funnel_id ) ) { |
| 1250 |
$this->funnel = new WFFN_Funnel( $funnel_id ); |
| 1251 |
|
| 1252 |
} |
| 1253 |
} |
| 1254 |
|
| 1255 |
public static function get_template_filter() { |
| 1256 |
|
| 1257 |
$options = array( |
| 1258 |
'all' => __( 'All', 'funnel-builder' ), |
| 1259 |
'sales' => __( 'Sales', 'funnel-builder' ), |
| 1260 |
'optin' => __( 'Optin', 'funnel-builder' ), |
| 1261 |
); |
| 1262 |
|
| 1263 |
return $options; |
| 1264 |
} |
| 1265 |
|
| 1266 |
|
| 1267 |
public function get_license_status() { |
| 1268 |
$license_key = WFFN_Core()->remote_importer->get_license_key( true ); |
| 1269 |
|
| 1270 |
if ( empty( $license_key ) ) { |
| 1271 |
return false; |
| 1272 |
} elseif ( isset( $license_key['manually_deactivated'] ) && 1 === $license_key['manually_deactivated'] ) { |
| 1273 |
return 'deactivated'; |
| 1274 |
} elseif ( isset( $license_key['expired'] ) && 1 === $license_key['expired'] ) { |
| 1275 |
return 'expired'; |
| 1276 |
} elseif ( isset( $license_key['activated'] ) && 0 === $license_key['activated'] ) { |
| 1277 |
return 'not-active'; |
| 1278 |
} |
| 1279 |
|
| 1280 |
return true; |
| 1281 |
} |
| 1282 |
|
| 1283 |
public function is_license_active() { |
| 1284 |
return true === $this->get_license_status(); |
| 1285 |
} |
| 1286 |
|
| 1287 |
|
| 1288 |
/** |
| 1289 |
* @hooked over `admin_enqueue_scripts` |
| 1290 |
* Check the environment and register appropiate node for the breadcrumb to process |
| 1291 |
* @since 1.0.0 |
| 1292 |
*/ |
| 1293 |
public function maybe_register_breadcrumb_nodes() { |
| 1294 |
$single_link = ''; |
| 1295 |
$funnel = null; |
| 1296 |
/** |
| 1297 |
* IF its experiment builder UI |
| 1298 |
*/ |
| 1299 |
if ( $this->is_wffn_flex_page() ) { |
| 1300 |
|
| 1301 |
$funnel = $this->get_funnel(); |
| 1302 |
|
| 1303 |
} else { |
| 1304 |
|
| 1305 |
/** |
| 1306 |
* its its a page where experiment page is a referrer |
| 1307 |
*/ |
| 1308 |
$get_ref = filter_input( INPUT_GET, 'funnel_id', FILTER_UNSAFE_RAW ); //phpcs:ignore WordPressVIPMinimum.Security.PHPFilterFunctions.RestrictedFilter |
| 1309 |
$get_ref = apply_filters( 'maybe_setup_funnel_for_breadcrumb', $get_ref ); |
| 1310 |
if ( ! empty( $get_ref ) ) { |
| 1311 |
$funnel = $this->get_funnel( $get_ref ); |
| 1312 |
if ( absint( $funnel->get_id() ) === WFFN_Common::get_store_checkout_id() ) { |
| 1313 |
$single_link = WFFN_Common::get_store_checkout_edit_link(); |
| 1314 |
} else { |
| 1315 |
$single_link = WFFN_Common::get_funnel_edit_link( $funnel->get_id() ); |
| 1316 |
} |
| 1317 |
} |
| 1318 |
} |
| 1319 |
|
| 1320 |
/** |
| 1321 |
* Register nodes |
| 1322 |
*/ |
| 1323 |
if ( ! empty( $funnel ) && null === filter_input( INPUT_GET, 'bwf_exp_ref', FILTER_UNSAFE_RAW ) ) { //phpcs:ignore WordPressVIPMinimum.Security.PHPFilterFunctions.RestrictedFilter |
| 1324 |
|
| 1325 |
BWF_Admin_Breadcrumbs::register_node( |
| 1326 |
array( |
| 1327 |
'text' => WFFN_Core()->admin->maybe_empty_title( $funnel->get_title() ), |
| 1328 |
'link' => $single_link, |
| 1329 |
) |
| 1330 |
); |
| 1331 |
BWF_Admin_Breadcrumbs::register_ref( 'funnel_id', $funnel->get_id() ); |
| 1332 |
|
| 1333 |
} |
| 1334 |
} |
| 1335 |
|
| 1336 |
|
| 1337 |
public function get_date_format() { |
| 1338 |
return get_option( 'date_format', '' ) . ' ' . get_option( 'time_format', '' ); |
| 1339 |
} |
| 1340 |
|
| 1341 |
/** |
| 1342 |
* @return array |
| 1343 |
*/ |
| 1344 |
public function get_funnels( $args = array() ) { |
| 1345 |
global $wpdb; |
| 1346 |
$is_total_query_required = true; |
| 1347 |
if ( isset( $args['offset'] ) && ! empty( $args['offset'] ) ) { |
| 1348 |
$is_total_query_required = false; |
| 1349 |
} |
| 1350 |
if ( isset( $args['s'] ) ) { |
| 1351 |
$search_str = wffn_clean( $args['s'] ); |
| 1352 |
} else { |
| 1353 |
$search_str = isset( $_REQUEST['s'] ) ? wffn_clean( wp_unslash( $_REQUEST['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 1354 |
} |
| 1355 |
$need_draft_count = $args['need_draft_count'] ?? false; |
| 1356 |
if ( isset( $args['status'] ) ) { |
| 1357 |
$status = wffn_clean( $args['status'] ); |
| 1358 |
} else { |
| 1359 |
$status = isset( $_REQUEST['status'] ) ? wffn_clean( wp_unslash( $_REQUEST['status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 1360 |
} |
| 1361 |
$args['meta'] = isset( $args['meta'] ) ? $args['meta'] : array(); |
| 1362 |
$limit = isset( $args['limit'] ) ? absint( $args['limit'] ) : $this->posts_per_page(); |
| 1363 |
|
| 1364 |
$sql_query = ' FROM {table_name}'; |
| 1365 |
|
| 1366 |
$args = apply_filters( 'wffn_funnels_args_query', $args ); |
| 1367 |
|
| 1368 |
if ( isset( $args['meta'] ) && is_array( $args['meta'] ) && ! empty( $args['meta'] ) && ! isset( $args['meta']['compare'] ) ) { |
| 1369 |
$args['meta']['compare'] = '='; |
| 1370 |
} |
| 1371 |
|
| 1372 |
/* |
| 1373 |
* Trying to add join in query base on meta |
| 1374 |
*/ |
| 1375 |
if ( ! empty( $args['meta'] ) ) { |
| 1376 |
if ( $args['meta']['compare'] === 'NOT_EXISTS' ) { |
| 1377 |
$sql_query .= ' LEFT JOIN '; |
| 1378 |
} else { |
| 1379 |
$sql_query .= ' INNER JOIN '; |
| 1380 |
} |
| 1381 |
$sql_query .= '{table_name_meta} ON ( {table_name}.id = {table_name_meta}.bwf_funnel_id '; |
| 1382 |
if ( $args['meta']['compare'] === 'NOT_EXISTS' ) { |
| 1383 |
$sql_query .= $wpdb->prepare( 'AND {table_name_meta}.meta_key = %s', $args['meta']['key'] ); |
| 1384 |
} |
| 1385 |
$sql_query .= ')'; |
| 1386 |
|
| 1387 |
} |
| 1388 |
|
| 1389 |
/* |
| 1390 |
* Sort list with meta key. |
| 1391 |
*/ |
| 1392 |
if ( ! empty( $args['order_by_meta'] ) ) { |
| 1393 |
$sql_query .= $wpdb->prepare( |
| 1394 |
' LEFT JOIN {table_name_meta} as order_by_meta |
| 1395 |
ON ( {table_name}.id = order_by_meta.bwf_funnel_id AND order_by_meta.meta_key = %s )', |
| 1396 |
$args['order_by_meta']['key'] |
| 1397 |
); |
| 1398 |
} |
| 1399 |
if ( ! empty( $args['categories'] ) && is_array( $args['categories'] ) ) { |
| 1400 |
$sql_query .= ' INNER JOIN {table_name_meta} AS cat_meta ON ( {table_name}.id = cat_meta.bwf_funnel_id AND cat_meta.meta_key = "wffn_funnel_category" )'; |
| 1401 |
} |
| 1402 |
/* |
| 1403 |
* where clause start here in query |
| 1404 |
*/ |
| 1405 |
$sql_query .= ' WHERE 1=1'; |
| 1406 |
|
| 1407 |
if ( ! empty( $args['categories'] ) && is_array( $args['categories'] ) ) { |
| 1408 |
$cat_conditions = array(); |
| 1409 |
|
| 1410 |
foreach ( $args['categories'] as $category ) { |
| 1411 |
$clean_category = sanitize_title( $category ); |
| 1412 |
$cat_conditions[] = $wpdb->prepare( '(cat_meta.meta_value LIKE %s)', "%$clean_category%" ); |
| 1413 |
} |
| 1414 |
|
| 1415 |
$sql_query .= ' AND (' . implode( ' OR ', $cat_conditions ) . ')'; |
| 1416 |
} |
| 1417 |
|
| 1418 |
if ( ! empty( $status ) && 'all' !== $status ) { |
| 1419 |
$status = ( 'live' === $status ) ? 1 : 0; |
| 1420 |
$sql_query .= $wpdb->prepare( ' AND `status` = %d', $status ); |
| 1421 |
} |
| 1422 |
|
| 1423 |
if ( ! empty( $search_str ) ) { |
| 1424 |
$sql_query .= $wpdb->prepare( ' AND ( `title` LIKE %s OR `desc` LIKE %s )', '%' . $search_str . '%', '%' . $search_str . '%' ); |
| 1425 |
} |
| 1426 |
if ( ! empty( $args['meta'] ) ) { |
| 1427 |
if ( $args['meta']['compare'] === 'NOT_EXISTS' ) { |
| 1428 |
|
| 1429 |
$sql_query .= ' AND ({table_name_meta}.bwf_funnel_id IS NULL) '; |
| 1430 |
if ( false === $is_total_query_required ) { |
| 1431 |
$sql_query .= ' GROUP BY {table_name}.id'; |
| 1432 |
|
| 1433 |
} |
| 1434 |
} else { |
| 1435 |
$sql_query .= $wpdb->prepare( ' AND ( {table_name_meta}.meta_key = %s AND {table_name_meta}.meta_value = %s )', $args['meta']['key'], $args['meta']['value'] ); |
| 1436 |
} |
| 1437 |
} |
| 1438 |
|
| 1439 |
if ( ! empty( $args['order_by_meta'] ) ) { |
| 1440 |
$sql_query .= ' ORDER BY |
| 1441 |
CASE WHEN order_by_meta.meta_value IS NULL THEN 1 ELSE 0 END, |
| 1442 |
order_by_meta.meta_value ' . ( isset( $args['order_by_meta']['order'] ) && 'ASC' === strtoupper( $args['order_by_meta']['order'] ) ? 'ASC' : 'DESC' ); |
| 1443 |
} else { |
| 1444 |
$sql_query .= ' ORDER BY {table_name}.id DESC'; |
| 1445 |
} |
| 1446 |
|
| 1447 |
if ( false === $is_total_query_required ) { |
| 1448 |
$sql_query .= ' LIMIT ' . absint( $args['offset'] ) . ', ' . $limit; |
| 1449 |
} else { |
| 1450 |
$found_funnels = WFFN_Core()->get_dB()->get_results( 'SELECT count({table_name}.id) as count ' . $sql_query ); |
| 1451 |
$sql_query .= ' LIMIT ' . 0 . ', ' . $limit; |
| 1452 |
} |
| 1453 |
$funnel_ids = WFFN_Core()->get_dB()->get_results( 'SELECT {table_name}.id as funnel_id ' . $sql_query ); |
| 1454 |
$items = array(); |
| 1455 |
|
| 1456 |
if ( isset( $args['search_filter'] ) ) { |
| 1457 |
foreach ( $funnel_ids as $funnel_id ) { |
| 1458 |
$funnel = new WFFN_Funnel( $funnel_id['funnel_id'] ); |
| 1459 |
$item = array( |
| 1460 |
'id' => $funnel->get_id(), |
| 1461 |
'name' => $funnel->get_title(), |
| 1462 |
); |
| 1463 |
$items[] = $item; |
| 1464 |
} |
| 1465 |
|
| 1466 |
return $items; |
| 1467 |
|
| 1468 |
} else { |
| 1469 |
foreach ( $funnel_ids as $funnel_id ) { |
| 1470 |
$funnel = new WFFN_Funnel( $funnel_id['funnel_id'] ); |
| 1471 |
$steps = $funnel->get_steps(); |
| 1472 |
$view = ( is_array( $steps ) && count( $steps ) > 0 ) ? get_permalink( $steps[0]['id'] ) : ''; |
| 1473 |
if ( false !== $need_draft_count || isset( $args['need_steps_data'] ) ) { |
| 1474 |
$this->parse_funnels_step_ids( $funnel ); |
| 1475 |
} |
| 1476 |
$item = array( |
| 1477 |
'id' => $funnel->get_id(), |
| 1478 |
'title' => $funnel->get_title(), |
| 1479 |
'desc' => $funnel->get_desc(), |
| 1480 |
'date_added' => $funnel->get_date_added(), |
| 1481 |
'last_update' => $funnel->get_last_update_date(), |
| 1482 |
'steps' => ( is_array( $this->step_count_against_fid ) && count( $this->step_count_against_fid ) > 0 ) ? count( array_keys( $this->step_count_against_fid, absint( $funnel->get_id() ), true ) ) : 0, |
| 1483 |
//phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict |
| 1484 |
'view_link' => $view, |
| 1485 |
'categories' => ( defined( 'WFFN_PRO_VERSION' ) ) ? $funnel->get_category() : array(), |
| 1486 |
); |
| 1487 |
if ( isset( $args['need_steps_data'] ) ) { |
| 1488 |
$item['steps_data'] = $steps; |
| 1489 |
} |
| 1490 |
if ( ! isset( $args['context'] ) || 'listing' !== $args['context'] ) { |
| 1491 |
$item['__funnel'] = $funnel; |
| 1492 |
} |
| 1493 |
$items[] = $item; |
| 1494 |
} |
| 1495 |
if ( true === $is_total_query_required ) { |
| 1496 |
$found_posts = array( 'found_posts' => (int) $found_funnels[0]['count'] ); |
| 1497 |
|
| 1498 |
} else { |
| 1499 |
$found_posts = array(); |
| 1500 |
|
| 1501 |
} |
| 1502 |
if ( false !== $need_draft_count ) { |
| 1503 |
$draft_counts = $this->get_draft_steps(); |
| 1504 |
$items = array_map( |
| 1505 |
function ( $item ) use ( $draft_counts ) { |
| 1506 |
$item['draft_count'] = $draft_counts[ $item['id'] ] ?? 0; |
| 1507 |
|
| 1508 |
return $item; |
| 1509 |
}, |
| 1510 |
$items |
| 1511 |
); |
| 1512 |
|
| 1513 |
} |
| 1514 |
$found_posts['items'] = $items; |
| 1515 |
|
| 1516 |
return apply_filters( 'wffn_funnels_lists', $found_posts ); |
| 1517 |
} |
| 1518 |
} |
| 1519 |
|
| 1520 |
/** |
| 1521 |
* @param WFFN_Funnel $funnel |
| 1522 |
* |
| 1523 |
* @return void |
| 1524 |
*/ |
| 1525 |
private function parse_funnels_step_ids( $funnel ) { |
| 1526 |
$steps = $funnel->get_steps(); |
| 1527 |
$funnel_id = $funnel->get_id(); |
| 1528 |
foreach ( $steps as $step ) { |
| 1529 |
|
| 1530 |
$step_id = $step['id']; |
| 1531 |
$this->step_against_fid[ $step_id ] = $funnel_id; |
| 1532 |
$this->step_count_against_fid[ absint( $step_id ) ] = absint( $funnel_id ); |
| 1533 |
|
| 1534 |
/** |
| 1535 |
* Handle case of upsells separately |
| 1536 |
*/ |
| 1537 |
if ( $step['type'] === 'wc_upsells' && WFFN_Core()->steps->get_integration_object( 'wc_upsells' ) instanceof WFFN_Step ) { |
| 1538 |
$funnel_offers = WFOCU_Core()->funnels->get_funnel_steps( $step['id'] ); |
| 1539 |
if ( ! empty( $funnel_offers ) && count( $funnel_offers ) > 1 ) { |
| 1540 |
$offer_ids = wp_list_pluck( $funnel_offers, 'id' ); |
| 1541 |
|
| 1542 |
$count = 0; |
| 1543 |
foreach ( $offer_ids as $offer_id ) { |
| 1544 |
$this->step_against_fid[ absint( $offer_id ) ] = $funnel_id; |
| 1545 |
/** |
| 1546 |
* skip first offer in step listing |
| 1547 |
*/ |
| 1548 |
if ( 0 !== $count ) { |
| 1549 |
$this->step_count_against_fid[ absint( $offer_id ) ] = absint( $funnel_id ); |
| 1550 |
} |
| 1551 |
++$count; |
| 1552 |
} |
| 1553 |
} |
| 1554 |
} |
| 1555 |
} |
| 1556 |
} |
| 1557 |
|
| 1558 |
private function get_draft_steps() { |
| 1559 |
if ( empty( $this->step_against_fid ) ) { |
| 1560 |
return array(); |
| 1561 |
} |
| 1562 |
|
| 1563 |
$step_ids = array_map( 'absint', array_keys( $this->step_against_fid ) ); |
| 1564 |
|
| 1565 |
global $wpdb; |
| 1566 |
$results = $wpdb->get_results( |
| 1567 |
$wpdb->prepare( |
| 1568 |
"SELECT ID FROM {$wpdb->prefix}posts WHERE 1=1 AND post_status != %s and id IN ( " . implode( ', ', array_fill( 0, count( $step_ids ), '%d' ) ) . ' )', |
| 1569 |
array_merge( array( 'publish' ), $step_ids ) |
| 1570 |
), |
| 1571 |
ARRAY_A |
| 1572 |
); |
| 1573 |
$draft_count = array(); |
| 1574 |
foreach ( $results as $result ) { |
| 1575 |
$step_id = $result['ID']; |
| 1576 |
$funnel_id = $this->step_against_fid[ $step_id ]; |
| 1577 |
if ( ! isset( $draft_count[ $funnel_id ] ) ) { |
| 1578 |
$draft_count[ $funnel_id ] = 1; |
| 1579 |
} else { |
| 1580 |
++$draft_count[ $funnel_id ]; |
| 1581 |
} |
| 1582 |
} |
| 1583 |
|
| 1584 |
return $draft_count; |
| 1585 |
} |
| 1586 |
|
| 1587 |
public function posts_per_page() { |
| 1588 |
return 20; |
| 1589 |
} |
| 1590 |
|
| 1591 |
|
| 1592 |
public function hide_from_menu() { |
| 1593 |
global $submenu; |
| 1594 |
foreach ( $submenu as $key => $men ) { |
| 1595 |
if ( 'woofunnels' !== $key ) { |
| 1596 |
continue; |
| 1597 |
} |
| 1598 |
foreach ( $men as $k => $d ) { |
| 1599 |
if ( 'woofunnels-settings' === $d[2] ) { |
| 1600 |
unset( $submenu[ $key ][ $k ] ); |
| 1601 |
} |
| 1602 |
} |
| 1603 |
} |
| 1604 |
} |
| 1605 |
|
| 1606 |
|
| 1607 |
/** |
| 1608 |
* Adding landing pages in homepage display settings |
| 1609 |
* |
| 1610 |
* @param $pages |
| 1611 |
* @param $args |
| 1612 |
* |
| 1613 |
* @return array |
| 1614 |
*/ |
| 1615 |
public function add_landing_in_home_pages( $pages, $args ) { |
| 1616 |
if ( is_array( $args ) && isset( $args['name'] ) && 'page_on_front' !== $args['name'] && '_customize-dropdown-pages-page_on_front' !== $args['name'] ) { |
| 1617 |
return $pages; |
| 1618 |
} |
| 1619 |
|
| 1620 |
if ( is_array( $args ) && isset( $args['name'] ) && ( 'page_on_front' === $args['name'] || '_customize-dropdown-pages-page_on_front' === $args['name'] ) ) { |
| 1621 |
$landing_pages = get_posts( |
| 1622 |
array( //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_posts |
| 1623 |
'post_type' => WFFN_Core()->landing_pages->get_post_type_slug(), |
| 1624 |
'numberposts' => 100, |
| 1625 |
'post_status' => 'publish', |
| 1626 |
) |
| 1627 |
); |
| 1628 |
|
| 1629 |
$optin_pages = get_posts( |
| 1630 |
array( //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_posts |
| 1631 |
'post_type' => WFOPP_Core()->optin_pages->get_post_type_slug(), |
| 1632 |
'numberposts' => 100, |
| 1633 |
'post_status' => 'publish', |
| 1634 |
) |
| 1635 |
); |
| 1636 |
|
| 1637 |
$pages = array_merge( $pages, $landing_pages, $optin_pages ); |
| 1638 |
} |
| 1639 |
|
| 1640 |
return $pages; |
| 1641 |
} |
| 1642 |
|
| 1643 |
|
| 1644 |
public function admin_footer_text( $footer_text ) { |
| 1645 |
if ( false === WFFN_Role_Capability::get_instance()->user_access( 'funnel', 'read' ) ) { |
| 1646 |
return $footer_text; |
| 1647 |
} |
| 1648 |
|
| 1649 |
$current_screen = get_current_screen(); |
| 1650 |
$wffn_pages = array( 'woofunnels_page_bwf', 'woofunnels_page_wffn-settings' ); |
| 1651 |
|
| 1652 |
// Check to make sure we're on a WooFunnels admin page. |
| 1653 |
if ( isset( $current_screen->id ) && apply_filters( 'bwf_funnels_funnels_display_admin_footer_text', in_array( $current_screen->id, $wffn_pages, true ), $current_screen->id ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 1654 |
// Change the footer text. |
| 1655 |
$footer_text = sprintf( __( 'Over %1$s 5 star reviews show that FunnelKit users trust our top-rated support for their online business. Do you need help? <a href="%2$s" target="_blank"><b>Contact FunnelKit Support</b></a>', 'funnel-builder' ), WFFN_REVIEW_RATING_COUNT . '+', 'https://funnelkit.com/support/?utm_source=WordPress&utm_medium=Support+Footer&utm_campaign=FB+Lite+Plugin' ); |
| 1656 |
|
| 1657 |
} |
| 1658 |
|
| 1659 |
return $footer_text; |
| 1660 |
} |
| 1661 |
|
| 1662 |
public function maybe_show_notices() { |
| 1663 |
|
| 1664 |
global $wffn_notices; |
| 1665 |
foreach ( $wffn_notices as $notice ) { |
| 1666 |
echo wp_kses_post( $notice ); |
| 1667 |
} |
| 1668 |
} |
| 1669 |
|
| 1670 |
public function remove_all() { |
| 1671 |
remove_all_actions( 'admin_notices' ); |
| 1672 |
remove_all_actions( 'all_admin_notices' ); |
| 1673 |
remove_all_actions( 'user_admin_notices' ); |
| 1674 |
remove_all_actions( 'network_admin_notices' ); |
| 1675 |
} |
| 1676 |
|
| 1677 |
/** |
| 1678 |
* Hooked over 'plugin_action_links_{PLUGIN_BASENAME}' WordPress hook to add deactivate popup support & add PRO link |
| 1679 |
* |
| 1680 |
* @param array $links array of existing links |
| 1681 |
* |
| 1682 |
* @return array modified array |
| 1683 |
*/ |
| 1684 |
public function plugin_actions( $links ) { |
| 1685 |
if ( isset( $links['deactivate'] ) ) { |
| 1686 |
$links['deactivate'] .= '<i class="woofunnels-slug" data-slug="' . WFFN_PLUGIN_BASENAME . '"></i>'; |
| 1687 |
} |
| 1688 |
if ( ! defined( 'WFFN_PRO_VERSION' ) ) { |
| 1689 |
$link = add_query_arg( |
| 1690 |
array( |
| 1691 |
'utm_source' => 'WordPress', |
| 1692 |
'utm_medium' => 'All+Plugins', |
| 1693 |
'utm_campaign' => 'FB+Lite+Plugin', |
| 1694 |
'utm_content' => WFFN_VERSION, |
| 1695 |
), |
| 1696 |
$this->get_pro_link() |
| 1697 |
); |
| 1698 |
$links = array_merge( |
| 1699 |
array( |
| 1700 |
'pro_upgrade' => '<a href="' . $link . '" target="_blank" style="color: #1da867 !important;font-weight:600">' . __( 'Upgrade to Pro', 'funnel-builder' ) . '</a>', |
| 1701 |
), |
| 1702 |
$links |
| 1703 |
); |
| 1704 |
} |
| 1705 |
|
| 1706 |
return $links; |
| 1707 |
} |
| 1708 |
|
| 1709 |
/** |
| 1710 |
* Initiate WFFN_Background_Importer class if ActionScheduler class doesn't exist |
| 1711 |
* |
| 1712 |
* @see woofunnels_maybe_update_customer_database() |
| 1713 |
*/ |
| 1714 |
public function wffn_maybe_init_background_updater() { |
| 1715 |
if ( class_exists( 'WFFN_Background_Importer' ) ) { |
| 1716 |
|
| 1717 |
$this->wffn_updater = new WFFN_Background_Importer(); |
| 1718 |
} |
| 1719 |
} |
| 1720 |
|
| 1721 |
/** |
| 1722 |
* @hooked over `admin_init` |
| 1723 |
* This method takes care of template importing |
| 1724 |
* Checks whether there is a need to import |
| 1725 |
* Iterates over define callbacks and passes it to background updater class |
| 1726 |
* Updates templates for all steps of the funnels |
| 1727 |
*/ |
| 1728 |
public function wffn_maybe_run_templates_importer() { |
| 1729 |
if ( is_null( $this->wffn_updater ) ) { |
| 1730 |
return; |
| 1731 |
} |
| 1732 |
$funnel_id = get_option( '_wffn_scheduled_funnel_id', 0 ); |
| 1733 |
|
| 1734 |
if ( $funnel_id > 0 ) { // WPCS: input var ok, CSRF ok. |
| 1735 |
|
| 1736 |
$task = 'wffn_maybe_import_funnel_in_background'; // Scanning order table and updating customer tables |
| 1737 |
$this->wffn_updater->push_to_queue( $task ); |
| 1738 |
BWF_Logger::get_instance()->log( '**************START Importing************', 'wffn_template_import' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r |
| 1739 |
$this->wffn_updater->save()->dispatch(); |
| 1740 |
BWF_Logger::get_instance()->log( 'First Dispatch completed', 'wffn_template_import' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r |
| 1741 |
} |
| 1742 |
} |
| 1743 |
|
| 1744 |
/** |
| 1745 |
* Delete wffn-wizard and redirect install |
| 1746 |
*/ |
| 1747 |
public function reset_wizard() { |
| 1748 |
if ( current_user_can( 'manage_options' ) && isset( $_GET['wffn_show_wizard_force'] ) && 'yes' === $_GET['wffn_show_wizard_force'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 1749 |
|
| 1750 |
delete_option( '_wffn_onboarding_completed' ); |
| 1751 |
delete_user_meta( get_current_user_id(), '_bwf_notifications_close' ); |
| 1752 |
wp_redirect( $this->wizard_url() ); |
| 1753 |
exit; |
| 1754 |
|
| 1755 |
} |
| 1756 |
} |
| 1757 |
|
| 1758 |
/** |
| 1759 |
* @return array |
| 1760 |
*/ |
| 1761 |
public function get_all_active_page_builders() { |
| 1762 |
$page_builders = array( 'gutenberg', 'elementor', 'divi', 'oxy', 'bricks' ); |
| 1763 |
|
| 1764 |
return $page_builders; |
| 1765 |
} |
| 1766 |
|
| 1767 |
/** |
| 1768 |
* Detect available page builders and set default settings |
| 1769 |
* |
| 1770 |
* This method checks for the following page builders in order of priority: |
| 1771 |
* - Elementor (elementor/elementor.php) |
| 1772 |
* - Divi (divi-builder/divi-builder.php or Divi theme) |
| 1773 |
* - Oxygen (oxygen/functions.php) |
| 1774 |
* - Bricks (Bricks theme) |
| 1775 |
* |
| 1776 |
* Only works if current default builder is 'elementor' or blank. |
| 1777 |
* |
| 1778 |
* @return string|null The detected page builder slug or null if none found |
| 1779 |
*/ |
| 1780 |
public function detect_and_set_default_page_builder( $force = false ) { |
| 1781 |
try { |
| 1782 |
$general_settings = BWF_Admin_General_Settings::get_instance(); |
| 1783 |
$current_default = $general_settings->get_option( 'default_selected_builder' ); |
| 1784 |
|
| 1785 |
// Only proceed if current default is 'elementor' or blank |
| 1786 |
if ( ! $force && ! empty( $current_default ) && $current_default !== 'elementor' ) { |
| 1787 |
return null; |
| 1788 |
} |
| 1789 |
|
| 1790 |
$detected_builder = null; |
| 1791 |
$page_builders = array( |
| 1792 |
'elementor' => array( |
| 1793 |
'plugin_file' => 'elementor/elementor.php', |
| 1794 |
'check_function' => function () { |
| 1795 |
return class_exists( '\Elementor\Plugin' ); |
| 1796 |
}, |
| 1797 |
), |
| 1798 |
'divi5' => array( |
| 1799 |
'plugin_file' => '', |
| 1800 |
'check_function' => function () { |
| 1801 |
return WFFN_Core()->page_builders->is_divi_theme_enabled() && function_exists( 'et_builder_d5_enabled' ) && et_builder_d5_enabled(); |
| 1802 |
}, |
| 1803 |
), |
| 1804 |
'divi' => array( |
| 1805 |
'plugin_file' => 'divi-builder/divi-builder.php', |
| 1806 |
'check_function' => function () { |
| 1807 |
return WFFN_Core()->page_builders->is_divi_theme_enabled(); |
| 1808 |
}, |
| 1809 |
), |
| 1810 |
'oxy' => array( |
| 1811 |
'plugin_file' => 'oxygen/functions.php', |
| 1812 |
'check_function' => function () { |
| 1813 |
return class_exists( 'OxygenElement' ); |
| 1814 |
}, |
| 1815 |
), |
| 1816 |
'bricks' => array( |
| 1817 |
'plugin_file' => '', |
| 1818 |
'check_function' => function () { |
| 1819 |
return WFFN_Core()->page_builders->is_bricks_theme_enabled(); |
| 1820 |
}, |
| 1821 |
), |
| 1822 |
); |
| 1823 |
|
| 1824 |
// Check each page builder |
| 1825 |
foreach ( $page_builders as $builder_slug => $builder_config ) { |
| 1826 |
$is_available = false; |
| 1827 |
|
| 1828 |
// Check if plugin file exists and is active |
| 1829 |
if ( ! empty( $builder_config['plugin_file'] ) ) { |
| 1830 |
$plugin_status = WFFN_Common::get_plugin_status( $builder_config['plugin_file'] ); |
| 1831 |
$is_available = ( 'activated' === $plugin_status ); |
| 1832 |
} |
| 1833 |
|
| 1834 |
// If no plugin file or plugin not active, check using custom function |
| 1835 |
if ( ! $is_available && isset( $builder_config['check_function'] ) ) { |
| 1836 |
$is_available = $builder_config['check_function'](); |
| 1837 |
} |
| 1838 |
|
| 1839 |
if ( $is_available ) { |
| 1840 |
$detected_builder = $builder_slug; |
| 1841 |
break; |
| 1842 |
} |
| 1843 |
} |
| 1844 |
|
| 1845 |
// Set default builder if detected, otherwise set to blank |
| 1846 |
if ( ! empty( $detected_builder ) ) { |
| 1847 |
// Only update if no default is set or if it's different from detected |
| 1848 |
if ( empty( $current_default ) || $current_default !== $detected_builder ) { |
| 1849 |
// Update using the proper settings method |
| 1850 |
$general_settings->update_global_settings_fields( array( 'default_selected_builder' => $detected_builder ) ); |
| 1851 |
|
| 1852 |
// Log the detection for debugging |
| 1853 |
BWF_Logger::get_instance()->log( |
| 1854 |
sprintf( 'Page builder detected and set as default: %s', $detected_builder ), |
| 1855 |
'wffn_page_builder_detection' |
| 1856 |
); |
| 1857 |
} |
| 1858 |
} else { |
| 1859 |
// No builder detected, set to blank if not already blank |
| 1860 |
if ( ! empty( $current_default ) ) { |
| 1861 |
// Update using the proper settings method |
| 1862 |
$general_settings->update_global_settings_fields( array( 'default_selected_builder' => '' ) ); |
| 1863 |
|
| 1864 |
// Log the reset for debugging |
| 1865 |
BWF_Logger::get_instance()->log( |
| 1866 |
'No page builder detected, default set to blank', |
| 1867 |
'wffn_page_builder_detection' |
| 1868 |
); |
| 1869 |
} |
| 1870 |
} |
| 1871 |
|
| 1872 |
return $detected_builder; |
| 1873 |
} catch ( \Throwable $e ) { |
| 1874 |
BWF_Logger::get_instance()->log( |
| 1875 |
sprintf( 'Error detecting page builder: %s', $e->getMessage() ), |
| 1876 |
'wffn_page_builder_detection' |
| 1877 |
); |
| 1878 |
return null; |
| 1879 |
} |
| 1880 |
} |
| 1881 |
|
| 1882 |
/** |
| 1883 |
* Get the currently detected/default page builder |
| 1884 |
* |
| 1885 |
* @return string The page builder slug or empty string if none set |
| 1886 |
*/ |
| 1887 |
public function get_detected_page_builder() { |
| 1888 |
try { |
| 1889 |
$general_settings = BWF_Admin_General_Settings::get_instance(); |
| 1890 |
$default_builder = $general_settings->get_option( 'default_selected_builder' ); |
| 1891 |
|
| 1892 |
// If no default is set, try to detect one |
| 1893 |
|
| 1894 |
$detected_builder = $this->detect_and_set_default_page_builder( true ); |
| 1895 |
$default_builder = ( $detected_builder !== null ) ? $detected_builder : ''; |
| 1896 |
|
| 1897 |
// Ensure we always return a string |
| 1898 |
return ( is_string( $default_builder ) ) ? $default_builder : ''; |
| 1899 |
} catch ( \Throwable $e ) { |
| 1900 |
BWF_Logger::get_instance()->log( |
| 1901 |
sprintf( 'Error getting detected page builder: %s', $e->getMessage() ), |
| 1902 |
'wffn_page_builder_detection' |
| 1903 |
); |
| 1904 |
return ''; |
| 1905 |
} |
| 1906 |
} |
| 1907 |
|
| 1908 |
|
| 1909 |
|
| 1910 |
/** |
| 1911 |
* Keep the menu open when editing the flows. |
| 1912 |
* Highlights the wanted admin (sub-) menu items for the CPT. |
| 1913 |
* |
| 1914 |
* @since 1.0.0 |
| 1915 |
*/ |
| 1916 |
public function menu_highlight() { |
| 1917 |
global $submenu_file; |
| 1918 |
$get_ref = filter_input( INPUT_GET, 'funnel_id' ); |
| 1919 |
if ( ! empty( $get_ref ) && absint( $get_ref ) === WFFN_Common::get_store_checkout_id() ) { |
| 1920 |
$submenu_file = 'admin.php?page=bwf&path=/store-checkout'; //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited |
| 1921 |
} elseif ( $get_ref ) { |
| 1922 |
$submenu_file = 'admin.php?page=bwf&path=/funnels'; //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited |
| 1923 |
} |
| 1924 |
} |
| 1925 |
|
| 1926 |
/** |
| 1927 |
* @param $query WP_Query |
| 1928 |
*/ |
| 1929 |
public function load_page_to_home_page( $query ) { |
| 1930 |
if ( $query->is_main_query() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) { |
| 1931 |
|
| 1932 |
$post_type = $query->get( 'post_type' ); |
| 1933 |
|
| 1934 |
$page_id = $query->get( 'page_id' ); |
| 1935 |
|
| 1936 |
if ( empty( $post_type ) && ! empty( $page_id ) ) { |
| 1937 |
$t_post = get_post( $page_id ); |
| 1938 |
if ( $t_post instanceof \WP_Post && in_array( $t_post->post_type, array( WFFN_Core()->landing_pages->get_post_type_slug(), WFOPP_Core()->optin_pages->get_post_type_slug() ), true ) ) { |
| 1939 |
$query->set( 'post_type', get_post_type( $page_id ) ); |
| 1940 |
} |
| 1941 |
} |
| 1942 |
} |
| 1943 |
} |
| 1944 |
|
| 1945 |
public function check_db_version() { |
| 1946 |
|
| 1947 |
include_once plugin_dir_path( WFFN_PLUGIN_FILE ) . 'admin/db/class-wffn-db-tables.php'; |
| 1948 |
$tables = WFFN_DB_Tables::get_instance(); |
| 1949 |
$tables->define_tables(); |
| 1950 |
$tables->add_if_needed(); |
| 1951 |
} |
| 1952 |
|
| 1953 |
/** |
| 1954 |
* @hooked over `admin_init` |
| 1955 |
* This method takes care of database updating process. |
| 1956 |
* Checks whether there is a need to update the database |
| 1957 |
* Iterates over define callbacks and passes it to background updater class |
| 1958 |
*/ |
| 1959 |
public function maybe_update_database_update() { |
| 1960 |
|
| 1961 |
try { |
| 1962 |
$task_list = array( |
| 1963 |
'3.3.1' => array( 'wffn_handle_store_checkout_config' ), |
| 1964 |
'3.3.3' => array( 'wffn_alter_conversion_table' ), |
| 1965 |
'3.3.4' => array( 'wffn_add_utm_columns_in_conversion_table' ), |
| 1966 |
'3.3.5' => array( 'wffn_update_migrate_data_for_currency_switcher' ), |
| 1967 |
'3.3.6' => array( 'wffn_alter_conversion_table_add_source' ), |
| 1968 |
'3.3.7' => array( 'wffn_update_email_default_settings' ), |
| 1969 |
'3.3.8' => array( 'wffn_set_default_value_in_autoload_option' ), |
| 1970 |
'3.3.9' => array( 'wffn_cleanup_data_for_conversion' ), |
| 1971 |
'3.4.0' => array( 'wffn_detect_and_set_default_page_builder' ), |
| 1972 |
'4.0.0' => array( 'wffn_add_order_number_column_in_conversion_table' ), |
| 1973 |
|
| 1974 |
); |
| 1975 |
|
| 1976 |
$task_list_for_new_install = array( |
| 1977 |
'wffn_update_email_default_settings', |
| 1978 |
'wffn_set_default_value_in_autoload_option', |
| 1979 |
'wffn_detect_and_set_default_page_builder', |
| 1980 |
); |
| 1981 |
$current_db_version = get_option( '_wffn_db_version', '0.0.0' ); |
| 1982 |
|
| 1983 |
/** |
| 1984 |
* 1. Fresh customer with no DB data. - |
| 1985 |
* - no task should run |
| 1986 |
* - direct update |
| 1987 |
* 2. Existing customer with db version less than current with task. |
| 1988 |
* - remaining tasks should run |
| 1989 |
* - update |
| 1990 |
* 3. Existing customer with db version less than current but no task. |
| 1991 |
* - no tasks should run |
| 1992 |
* - update |
| 1993 |
* 4. db version is update with current version. |
| 1994 |
* - return |
| 1995 |
* 5. db version is more than the current version. |
| 1996 |
* - return |
| 1997 |
*/ |
| 1998 |
|
| 1999 |
/** |
| 2000 |
* if the current db version is greater than or equal to the current version then no need to update the database |
| 2001 |
* case 4 and 5 |
| 2002 |
*/ |
| 2003 |
if ( version_compare( $current_db_version, WFFN_DB_VERSION, '>=' ) ) { |
| 2004 |
return; |
| 2005 |
} |
| 2006 |
|
| 2007 |
/** |
| 2008 |
* if the current db version is 0.0.0 |
| 2009 |
* case 1 |
| 2010 |
*/ |
| 2011 |
if ( $current_db_version === '0.0.0' ) { |
| 2012 |
foreach ( $task_list_for_new_install as $task ) { |
| 2013 |
call_user_func( $task ); |
| 2014 |
} |
| 2015 |
update_option( '_wffn_db_version', WFFN_DB_VERSION, true ); |
| 2016 |
|
| 2017 |
return; |
| 2018 |
} |
| 2019 |
|
| 2020 |
if ( ! empty( $task_list ) ) { |
| 2021 |
foreach ( $task_list as $version => $tasks ) { |
| 2022 |
if ( version_compare( $current_db_version, $version, '<' ) ) { |
| 2023 |
foreach ( $tasks as $update_callback ) { |
| 2024 |
|
| 2025 |
call_user_func( $update_callback ); |
| 2026 |
update_option( '_wffn_db_version', $version, true ); |
| 2027 |
$current_db_version = $version; |
| 2028 |
} |
| 2029 |
} |
| 2030 |
} |
| 2031 |
|
| 2032 |
/** |
| 2033 |
* If we do not have any task for the specific DB version then directly update option |
| 2034 |
*/ |
| 2035 |
if ( version_compare( $current_db_version, WFFN_DB_VERSION, '<' ) ) { |
| 2036 |
update_option( '_wffn_db_version', WFFN_DB_VERSION, true ); |
| 2037 |
} |
| 2038 |
|
| 2039 |
$this->clear_endpoints_from_cache(); |
| 2040 |
|
| 2041 |
} |
| 2042 |
} catch ( Exception |Error $e ) { |
| 2043 |
BWF_Logger::get_instance()->log( $e->getMessage(), 'wffn-db-update-exception', 'buildwoofunnels', true ); |
| 2044 |
} |
| 2045 |
} |
| 2046 |
|
| 2047 |
public function settings_config( $config ) { |
| 2048 |
/** |
| 2049 |
* The free plugin contributes only the upgrade panel. License fields are |
| 2050 |
* premium-only -- the license classes ship with the premium plugin, which |
| 2051 |
* registers them on this same filter at a later priority and drops the |
| 2052 |
* panel below. |
| 2053 |
*/ |
| 2054 |
$fields = array(); |
| 2055 |
|
| 2056 |
$fields[] = array( |
| 2057 |
'type' => 'label', |
| 2058 |
'key' => 'label_pro_control', |
| 2059 |
'label' => __( 'FunnelKit Funnel Builder Pro', 'funnel-builder' ), |
| 2060 |
'styleClasses' => array( 'wfacp_setting_track_and_events_start', 'bwf_wrap_custom_html_tracking_general' ), |
| 2061 |
); |
| 2062 |
array_unshift( |
| 2063 |
$fields, |
| 2064 |
array( |
| 2065 |
'key' => 'pro_control', |
| 2066 |
'type' => 'upgrade_pro', |
| 2067 |
'label' => __( '<strong>You are currently using FunnelKit Lite, which does not require a license.</strong><br/> To access more features, consider upgrading to FunnelKit PRO now.', 'funnel-builder' ), |
| 2068 |
'styleClasses' => array( 'wfacp_checkbox_wrap', 'wfacp_setting_track_and_events_end' ), |
| 2069 |
'hint' => '', |
| 2070 |
) |
| 2071 |
); |
| 2072 |
|
| 2073 |
return array_merge( $fields, $config ); |
| 2074 |
} |
| 2075 |
|
| 2076 |
|
| 2077 |
/** |
| 2078 |
* @param $link |
| 2079 |
* @param BWFABT_Experiment $experiment |
| 2080 |
* |
| 2081 |
* @return string |
| 2082 |
*/ |
| 2083 |
function maybe_modify_link( $link, $experiment ) { |
| 2084 |
|
| 2085 |
$get_control_id = $experiment->get_control(); |
| 2086 |
|
| 2087 |
$get_funnel_id = get_post_meta( $get_control_id, '_bwf_in_funnel', true ); |
| 2088 |
|
| 2089 |
if ( ! empty( $get_funnel_id ) ) { |
| 2090 |
|
| 2091 |
return WFFN_Common::get_experiment_edit_link( $get_funnel_id, $get_control_id ); |
| 2092 |
} |
| 2093 |
|
| 2094 |
return $link; |
| 2095 |
} |
| 2096 |
|
| 2097 |
/* |
| 2098 |
* @param $post_id |
| 2099 |
* @param $all_meta |
| 2100 |
* |
| 2101 |
* Return selected builder based on post meta when import page |
| 2102 |
* @return string[] |
| 2103 |
*/ |
| 2104 |
public function get_selected_template( $post_id, $all_meta ) { |
| 2105 |
$meta = ''; |
| 2106 |
if ( ! empty( $all_meta ) ) { |
| 2107 |
$meta = wp_list_pluck( $all_meta, 'meta_key' ); |
| 2108 |
} |
| 2109 |
|
| 2110 |
$template = array( |
| 2111 |
'selected' => 'wp_editor_1', |
| 2112 |
'selected_type' => 'wp_editor', |
| 2113 |
'template_active' => 'yes', |
| 2114 |
); |
| 2115 |
|
| 2116 |
$selected_template = apply_filters( 'wffn_set_selected_template_on_duplicate', array(), $post_id, $meta ); |
| 2117 |
|
| 2118 |
if ( is_array( $selected_template ) && count( $selected_template ) > 0 ) { |
| 2119 |
return $selected_template; |
| 2120 |
} |
| 2121 |
|
| 2122 |
if ( is_array( $meta ) ) { |
| 2123 |
if ( in_array( '_elementor_data', $meta, true ) ) { |
| 2124 |
$template['selected'] = 'elementor_1'; |
| 2125 |
$template['selected_type'] = 'elementor'; |
| 2126 |
|
| 2127 |
return $template; |
| 2128 |
} |
| 2129 |
if ( in_array( '_et_builder_version', $meta, true ) ) { |
| 2130 |
$template['selected'] = 'divi_1'; |
| 2131 |
$template['selected_type'] = 'divi'; |
| 2132 |
|
| 2133 |
return $template; |
| 2134 |
} |
| 2135 |
if ( in_array( 'ct_builder_shortcodes', $meta, true ) ) { |
| 2136 |
$template['selected'] = 'oxy_1'; |
| 2137 |
$template['selected_type'] = 'oxy'; |
| 2138 |
|
| 2139 |
return $template; |
| 2140 |
} |
| 2141 |
} |
| 2142 |
|
| 2143 |
if ( false !== strpos( get_post_field( 'post_content', $post_id ), '<!-- wp:' ) ) { |
| 2144 |
$template['selected'] = 'gutenberg_1'; |
| 2145 |
$template['selected_type'] = 'gutenberg'; |
| 2146 |
|
| 2147 |
return $template; |
| 2148 |
} |
| 2149 |
|
| 2150 |
return $template; |
| 2151 |
} |
| 2152 |
|
| 2153 |
public function get_pro_link() { |
| 2154 |
return esc_url( 'https://funnelkit.com/funnel-builder-lite-upgrade/' ); |
| 2155 |
} |
| 2156 |
|
| 2157 |
/** |
| 2158 |
* @param $post_id |
| 2159 |
* @param $post |
| 2160 |
* |
| 2161 |
* hooked over `before_delete_post` |
| 2162 |
* Checks if funnel step delete, then update associated funnel step meta |
| 2163 |
* |
| 2164 |
* @return void |
| 2165 |
*/ |
| 2166 |
public function delete_funnel_step_permanently( $post_id, $post ) { |
| 2167 |
|
| 2168 |
if ( is_null( $post ) ) { |
| 2169 |
return; |
| 2170 |
} |
| 2171 |
|
| 2172 |
if ( ! in_array( |
| 2173 |
$post->post_type, |
| 2174 |
array( |
| 2175 |
'wfacp_checkout', |
| 2176 |
'wffn_landing', |
| 2177 |
'wffn_ty', |
| 2178 |
'wffn_optin', |
| 2179 |
'wffn_oty', |
| 2180 |
), |
| 2181 |
true |
| 2182 |
) ) { |
| 2183 |
return; |
| 2184 |
} |
| 2185 |
|
| 2186 |
$get_funnel_id = get_post_meta( $post_id, '_bwf_in_funnel', true ); |
| 2187 |
|
| 2188 |
if ( empty( $get_funnel_id ) ) { |
| 2189 |
return; |
| 2190 |
} |
| 2191 |
|
| 2192 |
$funnel = new WFFN_Funnel( $get_funnel_id ); |
| 2193 |
|
| 2194 |
if ( $funnel instanceof WFFN_Funnel ) { |
| 2195 |
$funnel->delete_step( $get_funnel_id, $post_id ); |
| 2196 |
} |
| 2197 |
} |
| 2198 |
|
| 2199 |
/** |
| 2200 |
* @param $steps |
| 2201 |
* @param $funnel |
| 2202 |
* |
| 2203 |
* Removed step if not exists on funnel steps listing |
| 2204 |
* |
| 2205 |
* @return mixed |
| 2206 |
*/ |
| 2207 |
public function maybe_delete_funnel_step( $steps, $funnel ) { |
| 2208 |
|
| 2209 |
if ( ! $funnel instanceof WFFN_Funnel ) { |
| 2210 |
return $steps; |
| 2211 |
} |
| 2212 |
if ( is_array( $steps ) && count( $steps ) > 0 ) { |
| 2213 |
foreach ( $steps as $key => &$step ) { |
| 2214 |
|
| 2215 |
/** |
| 2216 |
* Skip if store funnel have native checkout |
| 2217 |
*/ |
| 2218 |
if ( absint( $funnel->get_id() ) === WFFN_Common::get_store_checkout_id() && WFFN_Common::store_native_checkout_slug() === $step['type'] ) { |
| 2219 |
continue; |
| 2220 |
} |
| 2221 |
|
| 2222 |
/** |
| 2223 |
* IF current step post not exist, then remove this step from funnel meta |
| 2224 |
*/ |
| 2225 |
if ( 0 <= $step['id'] && ! get_post( $step['id'] ) instanceof WP_Post ) { |
| 2226 |
unset( $steps[ $key ] ); |
| 2227 |
$funnel->delete_step( $funnel->get_id(), $step['id'] ); |
| 2228 |
} |
| 2229 |
} |
| 2230 |
} |
| 2231 |
|
| 2232 |
return $steps; |
| 2233 |
} |
| 2234 |
|
| 2235 |
/** |
| 2236 |
* Add a toolbar menu funnelkit and all submenu |
| 2237 |
* |
| 2238 |
* @param WP_Admin_Bar $wp_admin_bar |
| 2239 |
* |
| 2240 |
* @return void |
| 2241 |
* @throws Exception |
| 2242 |
*/ |
| 2243 |
public function add_menu_in_admin_bar( WP_Admin_Bar $wp_admin_bar ) { |
| 2244 |
$user = WFFN_Role_Capability::get_instance()->user_access( 'menu', 'read' ); |
| 2245 |
if ( ! $user ) { |
| 2246 |
return; |
| 2247 |
} |
| 2248 |
global $post; |
| 2249 |
$wp_admin_bar->add_node( |
| 2250 |
array( |
| 2251 |
'id' => 'wffn_funnel', |
| 2252 |
'title' => 'FunnelKit', |
| 2253 |
'href' => admin_url( 'admin.php?page=bwf&path=/funnels' ), |
| 2254 |
) |
| 2255 |
); |
| 2256 |
$admin_sub_nodes = array( |
| 2257 |
array( |
| 2258 |
'id' => 'fk_funnel', |
| 2259 |
'parent' => 'wffn_funnel', |
| 2260 |
'title' => __( 'Funnels', 'funnel-builder' ), |
| 2261 |
'href' => admin_url( 'admin.php?page=bwf&path=/funnels' ), |
| 2262 |
), |
| 2263 |
array( |
| 2264 |
'id' => 'fk_funnel_store_checkout', |
| 2265 |
'parent' => 'wffn_funnel', |
| 2266 |
'title' => __( 'Store Checkout', 'funnel-builder' ), |
| 2267 |
'href' => admin_url( 'admin.php?page=bwf&path=/store-checkout' ), |
| 2268 |
), |
| 2269 |
array( |
| 2270 |
'id' => 'fk_funnel_analytics', |
| 2271 |
'parent' => 'wffn_funnel', |
| 2272 |
'title' => __( 'Analytics', 'funnel-builder' ), |
| 2273 |
'href' => admin_url( 'admin.php?page=bwf&path=/analytics' ), |
| 2274 |
), |
| 2275 |
array( |
| 2276 |
'id' => 'fk_funnel_templates', |
| 2277 |
'parent' => 'wffn_funnel', |
| 2278 |
'title' => __( 'Templates', 'funnel-builder' ), |
| 2279 |
'href' => admin_url( 'admin.php?page=bwf&path=/templates' ), |
| 2280 |
), |
| 2281 |
array( |
| 2282 |
'id' => 'fk_funnel_settings', |
| 2283 |
'parent' => 'wffn_funnel', |
| 2284 |
'title' => __( 'Settings', 'funnel-builder' ), |
| 2285 |
'href' => admin_url( 'admin.php?page=bwf&path=/settings' ), |
| 2286 |
), |
| 2287 |
|
| 2288 |
); |
| 2289 |
|
| 2290 |
$wffn_steps = array( |
| 2291 |
'wffn_landing' => array( |
| 2292 |
'slug' => 'funnel-landing', |
| 2293 |
'title' => __( 'Edit Sales', 'funnel-builder' ), |
| 2294 |
), |
| 2295 |
'wfacp_checkout' => array( |
| 2296 |
'slug' => 'funnel-checkout', |
| 2297 |
'title' => __( 'Edit Checkout', 'funnel-builder' ), |
| 2298 |
), |
| 2299 |
'wfocu_offer' => array( |
| 2300 |
'slug' => 'funnel-offer', |
| 2301 |
'title' => __( 'Edit Offer', 'funnel-builder' ), |
| 2302 |
), |
| 2303 |
'wffn_ty' => array( |
| 2304 |
'slug' => 'funnel-thankyou', |
| 2305 |
'title' => __( 'Edit Thank You', 'funnel-builder' ), |
| 2306 |
), |
| 2307 |
'wffn_optin' => array( |
| 2308 |
'slug' => 'funnel-optin', |
| 2309 |
'title' => __( 'Edit Optin', 'funnel-builder' ), |
| 2310 |
), |
| 2311 |
'wffn_oty' => array( |
| 2312 |
'slug' => 'funnel-optin-confirmation', |
| 2313 |
'title' => __( 'Edit Optin Confirmation', 'funnel-builder' ), |
| 2314 |
), |
| 2315 |
); |
| 2316 |
if ( ! is_null( $post ) && ! empty( $post->post_type ) && isset( $wffn_steps[ $post->post_type ] ) ) { |
| 2317 |
$step = $wffn_steps[ $post->post_type ]; |
| 2318 |
|
| 2319 |
if ( 'wfocu_offer' === $post->post_type ) { |
| 2320 |
$upsell_id = get_post_meta( $post->ID, '_funnel_id', true ); |
| 2321 |
$funnel_id = get_post_meta( $upsell_id, '_bwf_in_funnel', true ); |
| 2322 |
} else { |
| 2323 |
$funnel_id = get_post_meta( $post->ID, '_bwf_in_funnel', true ); |
| 2324 |
} |
| 2325 |
if ( ! empty( $funnel_id ) && abs( $funnel_id ) !== 0 ) { |
| 2326 |
$is_store_checkout = ( method_exists( 'WFFN_Common', 'get_store_checkout_id' ) && absint( $funnel_id ) === WFFN_Common::get_store_checkout_id() ); |
| 2327 |
|
| 2328 |
if ( $is_store_checkout ) { |
| 2329 |
$store_checkout_map = array( |
| 2330 |
'funnel-checkout' => 'checkout', |
| 2331 |
'funnel-offer' => 'offer', |
| 2332 |
'funnel-thankyou' => 'thankyou', |
| 2333 |
); |
| 2334 |
$step_type = isset( $store_checkout_map[ $step['slug'] ] ) ? $store_checkout_map[ $step['slug'] ] : null; |
| 2335 |
|
| 2336 |
if ( null !== $step_type ) { |
| 2337 |
$funnel_link = BWF_Admin_Breadcrumbs::maybe_add_refs( |
| 2338 |
add_query_arg( |
| 2339 |
array( |
| 2340 |
'page' => 'bwf', |
| 2341 |
'path' => '/store-checkout', |
| 2342 |
), |
| 2343 |
admin_url( 'admin.php' ) |
| 2344 |
) |
| 2345 |
); |
| 2346 |
$step_link = BWF_Admin_Breadcrumbs::maybe_add_refs( |
| 2347 |
add_query_arg( |
| 2348 |
array( |
| 2349 |
'page' => 'bwf', |
| 2350 |
'path' => '/store-checkout/' . $step_type . '/' . $post->ID . '/design', |
| 2351 |
), |
| 2352 |
admin_url( 'admin.php' ) |
| 2353 |
) |
| 2354 |
); |
| 2355 |
$funnel_title = __( 'Edit Store Checkout', 'funnel-builder' ); |
| 2356 |
} else { |
| 2357 |
$is_store_checkout = false; |
| 2358 |
} |
| 2359 |
} |
| 2360 |
|
| 2361 |
if ( ! $is_store_checkout ) { |
| 2362 |
$funnel_link = BWF_Admin_Breadcrumbs::maybe_add_refs( |
| 2363 |
add_query_arg( |
| 2364 |
array( |
| 2365 |
'page' => 'bwf', |
| 2366 |
'path' => '/funnels/' . $funnel_id . '/steps', |
| 2367 |
), |
| 2368 |
admin_url( 'admin.php' ) |
| 2369 |
) |
| 2370 |
); |
| 2371 |
$step_link = BWF_Admin_Breadcrumbs::maybe_add_refs( |
| 2372 |
add_query_arg( |
| 2373 |
array( |
| 2374 |
'page' => 'bwf', |
| 2375 |
'path' => '/' . $step['slug'] . '/' . $post->ID . '/design', |
| 2376 |
'funnel_id' => $funnel_id, |
| 2377 |
), |
| 2378 |
admin_url( 'admin.php' ) |
| 2379 |
) |
| 2380 |
); |
| 2381 |
$funnel_title = __( 'Edit Funnel', 'funnel-builder' ); |
| 2382 |
} |
| 2383 |
|
| 2384 |
/** |
| 2385 |
* Add submenu for the frontend if its our step |
| 2386 |
*/ |
| 2387 |
$wp_admin_bar->add_menu( |
| 2388 |
array( |
| 2389 |
'id' => 'wffn_edit_funnel', |
| 2390 |
'parent' => 'wffn_funnel', |
| 2391 |
'title' => $funnel_title, |
| 2392 |
'href' => $funnel_link, |
| 2393 |
) |
| 2394 |
); |
| 2395 |
|
| 2396 |
$wp_admin_bar->add_menu( |
| 2397 |
array( |
| 2398 |
'id' => 'wffn_edit_step', |
| 2399 |
'parent' => 'wffn_funnel', |
| 2400 |
'title' => $step['title'], |
| 2401 |
'href' => $step_link, |
| 2402 |
) |
| 2403 |
); |
| 2404 |
?> |
| 2405 |
<style type="text/css"> |
| 2406 |
ul#wp-admin-bar-wffn_funnel-default li#wp-admin-bar-wffn_edit_step { |
| 2407 |
margin-bottom: 5px; |
| 2408 |
padding-bottom: 5px; |
| 2409 |
border-bottom: 1px dashed #65686b; |
| 2410 |
} |
| 2411 |
</style> |
| 2412 |
<?php |
| 2413 |
} |
| 2414 |
} |
| 2415 |
|
| 2416 |
/** |
| 2417 |
* Add all sub nodes which are static |
| 2418 |
*/ |
| 2419 |
foreach ( $admin_sub_nodes as $node ) { |
| 2420 |
$wp_admin_bar->add_menu( $node ); |
| 2421 |
} |
| 2422 |
|
| 2423 |
/** |
| 2424 |
* Possibly add sub nodes for different pro states |
| 2425 |
*/ |
| 2426 |
if ( ! defined( 'WFFN_PRO_VERSION' ) ) { |
| 2427 |
$link = add_query_arg( |
| 2428 |
array( |
| 2429 |
'utm_source' => 'WordPress', |
| 2430 |
'utm_medium' => 'Toolbar+Menu', |
| 2431 |
'utm_campaign' => 'FB+Lite+Plugin', |
| 2432 |
), |
| 2433 |
WFFN_Core()->admin->get_pro_link() |
| 2434 |
); |
| 2435 |
$wp_admin_bar->add_menu( |
| 2436 |
array( |
| 2437 |
'id' => 'wffn_funnel-additional_lite', |
| 2438 |
'parent' => 'wffn_funnel', |
| 2439 |
'title' => __( 'Upgrade to Pro', 'funnel-builder' ), |
| 2440 |
'href' => $link, |
| 2441 |
) |
| 2442 |
); |
| 2443 |
?> |
| 2444 |
<style type="text/css"> |
| 2445 |
ul#wp-admin-bar-wffn_funnel-default li#wp-admin-bar-wffn_funnel-additional_lite a.ab-item { |
| 2446 |
color: white; |
| 2447 |
background-color: #1DA867; |
| 2448 |
} |
| 2449 |
</style> |
| 2450 |
<?php |
| 2451 |
} |
| 2452 |
} |
| 2453 |
|
| 2454 |
/** |
| 2455 |
* @param $title |
| 2456 |
* set default post title if post tile empty |
| 2457 |
* |
| 2458 |
* @return mixed|string|null |
| 2459 |
*/ |
| 2460 |
public function maybe_empty_title( $title ) { |
| 2461 |
if ( empty( $title ) ) { |
| 2462 |
return __( '(no title)', 'funnel-builder' ); |
| 2463 |
} |
| 2464 |
|
| 2465 |
return $title; |
| 2466 |
} |
| 2467 |
|
| 2468 |
|
| 2469 |
public function wizard_url() { |
| 2470 |
return admin_url( 'admin.php?page=bwf&path=/user-setup' ); |
| 2471 |
} |
| 2472 |
|
| 2473 |
/** |
| 2474 |
* Filter CB to alter response data to pass connect link to the REST cll |
| 2475 |
* |
| 2476 |
* @param array $response |
| 2477 |
* @param string $basename |
| 2478 |
* |
| 2479 |
* @return mixed |
| 2480 |
*/ |
| 2481 |
public function maybe_add_auth_link_stripe( $response, $basename ) { |
| 2482 |
if ( 'funnelkit-stripe-woo-payment-gateway/funnelkit-stripe-woo-payment-gateway.php' === $basename ) { |
| 2483 |
$response['next_action'] = 'funnelkit-app/stripe-connect-link'; |
| 2484 |
} |
| 2485 |
|
| 2486 |
return $response; |
| 2487 |
} |
| 2488 |
|
| 2489 |
public function maybe_add_auth_link_square( $response, $basename ) { |
| 2490 |
if ( 'funnelkit-payment-gateway-square-for-woocommerce/funnelkit-square.php' === $basename ) { |
| 2491 |
$response['next_action'] = 'funnelkit-app/square-connect-link'; |
| 2492 |
} |
| 2493 |
|
| 2494 |
return $response; |
| 2495 |
} |
| 2496 |
|
| 2497 |
|
| 2498 |
/** |
| 2499 |
* Expiry date of the premium license, empty when there is none. |
| 2500 |
* |
| 2501 |
* Kept as a seam: the premium plugin calls this directly and supplies the |
| 2502 |
* value on the filter, since the free plugin ships no license data. |
| 2503 |
* |
| 2504 |
* @return string |
| 2505 |
*/ |
| 2506 |
public function get_license_expiry() { |
| 2507 |
return apply_filters( 'wffn_license_expiry', '' ); |
| 2508 |
} |
| 2509 |
public function get_license_basename_sha1() { |
| 2510 |
return array( |
| 2511 |
'checkout' => '742fc61c1b455e2b1efa4154a92da8fb7f9866d3', |
| 2512 |
'upsell' => 'e837ebc716ca979006da34eecdce9f650ced6bef', |
| 2513 |
'pro' => 'ffec4bb68f0841db41213ce12305aaef7e0237f3', |
| 2514 |
'basic' => 'e234ca9ec3e4856bb05ea9f8ec90e7f3831b05c5', |
| 2515 |
|
| 2516 |
); |
| 2517 |
} |
| 2518 |
|
| 2519 |
public function blocks_incompatible_switch_to_classic_cart_checkout( $is_rest = false ) { |
| 2520 |
|
| 2521 |
if ( ! class_exists( '\Automattic\WooCommerce\Blocks\BlockTypes\ClassicShortcode' ) || // Make sure WC version is at least 8.3. This class is added at version 8.3. |
| 2522 |
! current_user_can( 'manage_options' ) || ( empty( $is_rest ) && false === check_ajax_referer( 'wffn_blocks_incompatible_switch_to_classic', 'nonce', false ) ) ) { |
| 2523 |
return; |
| 2524 |
} |
| 2525 |
|
| 2526 |
$wc_cart_page = get_post( wc_get_page_id( 'cart' ) ); |
| 2527 |
$wc_checkout_page = get_post( wc_get_page_id( 'checkout' ) ); |
| 2528 |
|
| 2529 |
if ( has_block( 'woocommerce/checkout', $wc_checkout_page ) ) { |
| 2530 |
wp_update_post( |
| 2531 |
array( |
| 2532 |
'ID' => $wc_checkout_page->ID, |
| 2533 |
'post_content' => '<!-- wp:woocommerce/classic-shortcode {"shortcode":"checkout"} /-->', |
| 2534 |
) |
| 2535 |
); |
| 2536 |
} |
| 2537 |
|
| 2538 |
if ( has_block( 'woocommerce/cart', $wc_cart_page ) ) { |
| 2539 |
wp_update_post( |
| 2540 |
array( |
| 2541 |
'ID' => $wc_cart_page->ID, |
| 2542 |
'post_content' => '<!-- wp:woocommerce/classic-shortcode {"shortcode":"cart"} /-->', |
| 2543 |
) |
| 2544 |
); |
| 2545 |
} |
| 2546 |
|
| 2547 |
$userdata = get_user_meta( get_current_user_id(), '_bwf_notifications_close', true ); |
| 2548 |
$userdata = empty( $userdata ) && ! is_array( $userdata ) ? array() : $userdata; |
| 2549 |
$userdata[] = 'wc_block_incompat'; |
| 2550 |
update_user_meta( get_current_user_id(), '_bwf_notifications_close', array_values( array_unique( $userdata ) ) ); //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_update_user_meta |
| 2551 |
|
| 2552 |
if ( ! empty( $is_rest ) ) { |
| 2553 |
return rest_ensure_response( array( 'success' => true ) ); |
| 2554 |
|
| 2555 |
} |
| 2556 |
$redirect = isset( $_REQUEST['redirect'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect'] ) ) : null; |
| 2557 |
$redirect_url = $redirect && strpos( $redirect, '.php' ) ? admin_url( $redirect ) : null; |
| 2558 |
|
| 2559 |
wp_safe_redirect( $redirect_url ?? admin_url( 'admin.php?page=bwf&path=/funnels' ) ); |
| 2560 |
exit; |
| 2561 |
} |
| 2562 |
|
| 2563 |
/** |
| 2564 |
* AJAX dismiss admin notice. |
| 2565 |
* |
| 2566 |
* @since 1.1 |
| 2567 |
* @since 4.5.1 Add nonce verification when dismissing notices. |
| 2568 |
* @access public |
| 2569 |
*/ |
| 2570 |
public function ajax_dismiss_admin_notice() { |
| 2571 |
$notice_key = isset( $_REQUEST['nkey'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['nkey'] ) ) : ''; |
| 2572 |
|
| 2573 |
if ( |
| 2574 |
defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( 'manage_options' ) && $notice_key && isset( $_REQUEST['nonce'] ) && false !== check_ajax_referer( 'wp_wffn_dismiss_notice', 'nonce', false ) |
| 2575 |
|
| 2576 |
) { |
| 2577 |
|
| 2578 |
$userdata = get_user_meta( get_current_user_id(), '_bwf_notifications_close', true ); |
| 2579 |
$userdata = empty( $userdata ) && ! is_array( $userdata ) ? array() : $userdata; |
| 2580 |
$userdata[] = $notice_key; |
| 2581 |
|
| 2582 |
update_user_meta( get_current_user_id(), '_bwf_notifications_close', array_values( array_unique( $userdata ) ) ); //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_update_user_meta |
| 2583 |
// If primary sticky banner is dismissed, set 5-minute transient to hide admin notifications |
| 2584 |
// Secondary sticky banner does NOT trigger this transient - it will always show until dismissed |
| 2585 |
if ( strpos( $notice_key, 'sticky_' ) === 0 && strpos( $notice_key, 'secondary_sticky_' ) !== 0 ) { |
| 2586 |
set_transient( 'wffn_sticky_banner_dismissed_' . get_current_user_id(), $notice_key, 5 * MINUTE_IN_SECONDS ); |
| 2587 |
} |
| 2588 |
} |
| 2589 |
$redirect = isset( $_REQUEST['redirect'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect'] ) ) : null; |
| 2590 |
|
| 2591 |
$redirect_url = $redirect && strpos( $redirect, '.php' ) ? admin_url( $redirect ) : null; |
| 2592 |
|
| 2593 |
wp_safe_redirect( $redirect_url ?? admin_url( 'admin.php?page=bwf&path=/funnels' ) ); |
| 2594 |
exit; |
| 2595 |
} |
| 2596 |
|
| 2597 |
public function get_pro_activation_date() { |
| 2598 |
if ( ! defined( 'WFFN_PRO_VERSION' ) ) { |
| 2599 |
return ''; |
| 2600 |
} |
| 2601 |
$pro_activation_date = get_option( 'fk_fb_active_date', array() ); |
| 2602 |
if ( empty( $pro_activation_date ) || ! isset( $pro_activation_date['pro'] ) ) { |
| 2603 |
$date = new DateTime( 'now' ); |
| 2604 |
$date->modify( '-10 days' ); |
| 2605 |
$pro_activation_date['pro'] = $date->getTimestamp(); |
| 2606 |
update_option( 'fk_fb_active_date', $pro_activation_date, false ); |
| 2607 |
|
| 2608 |
return $date->format( 'Y-m-d H:i:s' ); |
| 2609 |
} |
| 2610 |
|
| 2611 |
return gmdate( 'Y-m-d H:i:s', $pro_activation_date['pro'] ); |
| 2612 |
} |
| 2613 |
|
| 2614 |
public function get_lite_activation_date() { |
| 2615 |
|
| 2616 |
$pro_activation_date = get_option( 'fk_fb_active_date', array() ); |
| 2617 |
if ( empty( $pro_activation_date ) || ! isset( $pro_activation_date['lite'] ) ) { |
| 2618 |
$date = new DateTime( 'now' ); |
| 2619 |
$pro_activation_date['lite'] = $date->getTimestamp(); |
| 2620 |
update_option( 'fk_fb_active_date', $pro_activation_date, false ); |
| 2621 |
|
| 2622 |
return $date->format( 'Y-m-d H:i:s' ); |
| 2623 |
} |
| 2624 |
|
| 2625 |
return gmdate( 'Y-m-d H:i:s', $pro_activation_date['lite'] ); |
| 2626 |
} |
| 2627 |
|
| 2628 |
|
| 2629 |
/** |
| 2630 |
* Get license related configuration to spread over all product to handle all features |
| 2631 |
* |
| 2632 |
* @param boolean $expiry_only true if only expiry related info need, else all info |
| 2633 |
* |
| 2634 |
* @return array a complete array for every product to handle license related info |
| 2635 |
*/ |
| 2636 |
/** |
| 2637 |
* License state consumed by this plugin, its React app and companion |
| 2638 |
* plugins (upsells, order bumps, cart). |
| 2639 |
* |
| 2640 |
* The free plugin holds no license data of its own; the premium plugin |
| 2641 |
* supplies the real payload on the wffn_license_config filter. The method |
| 2642 |
* itself stays because it is a cross-plugin contract that callers probe |
| 2643 |
* with method_exists(). |
| 2644 |
* |
| 2645 |
* The default returned here is just a static array kept for backward |
| 2646 |
* compatibility with the premium plugin; it carries a future expiry date |
| 2647 |
* so all gated features stay unlocked. It is not real license state. |
| 2648 |
* |
| 2649 |
* The default handed to the filter is deliberately fail-open. A premium |
| 2650 |
* build that predates this filter never answers it, so the default is |
| 2651 |
* what its front-end gates (WFFN_Step_WC_Upsells / WFFN_Substep_WC_Order_Bump / |
| 2652 |
* Cart Pro) see. An empty array would resolve to 'pro_without_license' in |
| 2653 |
* their get_current_app_state() and silently suppress every upsell, order |
| 2654 |
* bump and cart offer for a genuinely licensed store. So the default |
| 2655 |
* instead reports an active license (la === true) with a far-future |
| 2656 |
* expiry, keeping the older premium side working across the version-skew |
| 2657 |
* window; the moment premium is updated it overrides this default wholesale |
| 2658 |
* (its filter callback ignores the incoming value). On a free-only install |
| 2659 |
* nothing consumes this, so the default is inert there. |
| 2660 |
* |
| 2661 |
* @param bool $expiry_only Return only expiry information. |
| 2662 |
* @param bool $get_ad Include activation dates. |
| 2663 |
* |
| 2664 |
* @return array |
| 2665 |
*/ |
| 2666 |
public function get_license_config( $expiry_only = false, $get_ad = true ) { |
| 2667 |
/** |
| 2668 |
* Static backward-compatibility default for the premium plugin. |
| 2669 |
* |
| 2670 |
* This array exists purely so older premium builds (which do not answer |
| 2671 |
* the wffn_license_config filter) keep working. It is a fixed shape, not |
| 2672 |
* real license data -- the free plugin has none. The expiry date ('ed') |
| 2673 |
* is set to a future date on purpose so every gated feature (upsells, |
| 2674 |
* order bumps, cart) stays unlocked; a past/empty value would read as an |
| 2675 |
* expired/absent license and suppress those offers. |
| 2676 |
*/ |
| 2677 |
$far_future = gmdate( 'Y-m-d H:i:s', strtotime( '+10 years' ) ); // Future expiry -> keeps all features unlocked for older premium builds. |
| 2678 |
|
| 2679 |
if ( $expiry_only ) { |
| 2680 |
$default = array( |
| 2681 |
'f' => array( 'ed' => $far_future ), |
| 2682 |
'ck' => array( 'ed' => $far_future ), |
| 2683 |
'ul' => array( 'ed' => $far_future ), |
| 2684 |
'gp' => array( 2, 2 ), |
| 2685 |
); |
| 2686 |
} else { |
| 2687 |
$default = array( |
| 2688 |
'f' => array( |
| 2689 |
'la' => true, |
| 2690 |
'ed' => $far_future, |
| 2691 |
'e' => true, |
| 2692 |
), |
| 2693 |
'ck' => array( |
| 2694 |
'la' => true, |
| 2695 |
'ed' => $far_future, |
| 2696 |
'e' => true, |
| 2697 |
), |
| 2698 |
'ul' => array( |
| 2699 |
'la' => true, |
| 2700 |
'ed' => $far_future, |
| 2701 |
'e' => true, |
| 2702 |
), |
| 2703 |
'gp' => array( 2, 2 ), |
| 2704 |
); |
| 2705 |
} |
| 2706 |
|
| 2707 |
return apply_filters( 'wffn_license_config', $default, $expiry_only, $get_ad ); |
| 2708 |
} |
| 2709 |
|
| 2710 |
/** |
| 2711 |
* redirect checkout edit link on react screen when click on edit link from wc order screen |
| 2712 |
* |
| 2713 |
* @return void |
| 2714 |
*/ |
| 2715 |
public function redirect_checkout_edit_link_on_new_ui() { |
| 2716 |
$checkout_id = filter_input( INPUT_GET, 'wfacp_id', FILTER_VALIDATE_INT ); |
| 2717 |
if ( empty( $checkout_id ) ) { |
| 2718 |
return; |
| 2719 |
} |
| 2720 |
$funnel_id = get_post_meta( $checkout_id, '_bwf_in_funnel', true ); |
| 2721 |
if ( ! empty( $funnel_id ) && abs( $funnel_id ) > 0 ) { |
| 2722 |
// @codingStandardsIgnoreStart |
| 2723 |
$edit_link = add_query_arg( [ |
| 2724 |
'page' => 'bwf', |
| 2725 |
'path' => "/funnel-checkout/" . $checkout_id . "/design" |
| 2726 |
], admin_url( 'admin.php' ) ); |
| 2727 |
// @codingStandardsIgnoreEnd |
| 2728 |
wp_redirect( $edit_link ); |
| 2729 |
exit; |
| 2730 |
} |
| 2731 |
} |
| 2732 |
|
| 2733 |
public function maybe_force_redirect_to_wizard() { |
| 2734 |
|
| 2735 |
if ( false === $this->is_wizard_available( true ) ) { |
| 2736 |
return; |
| 2737 |
} |
| 2738 |
|
| 2739 |
wp_redirect( WFFN_Core()->admin->wizard_url() ); |
| 2740 |
exit; |
| 2741 |
} |
| 2742 |
|
| 2743 |
|
| 2744 |
/** |
| 2745 |
* Helper method to detect if the wizard is available to show |
| 2746 |
* Check all prior conditions before showing force wizard or notice |
| 2747 |
* |
| 2748 |
* @param bool $force check if it's a check for force wizard open |
| 2749 |
* |
| 2750 |
* @return bool true if all checks are passed, false otherwise |
| 2751 |
*/ |
| 2752 |
public function is_wizard_available( $force = false ) { |
| 2753 |
try { |
| 2754 |
$first_version = get_option( 'wffn_first_v', '0.0.0' ); |
| 2755 |
if ( $force !== false && false === version_compare( $first_version, WFFN_VERSION, '=' ) ) { |
| 2756 |
|
| 2757 |
/** |
| 2758 |
* bail out if old users versions |
| 2759 |
*/ |
| 2760 |
return false; |
| 2761 |
} |
| 2762 |
|
| 2763 |
if ( WFFN_Core()->admin_notifications->is_user_dismissed( get_current_user_id(), 'onboarding_wizard' ) ) { |
| 2764 |
/** |
| 2765 |
* This flag tells us that the wizard notice has already been dismissed |
| 2766 |
*/ |
| 2767 |
return false; |
| 2768 |
} |
| 2769 |
|
| 2770 |
$status = get_option( '_wffn_onboarding_completed', false ); |
| 2771 |
if ( false !== $status ) { |
| 2772 |
/** |
| 2773 |
* bail out if wizard started/skipped/completed |
| 2774 |
*/ |
| 2775 |
return false; |
| 2776 |
} |
| 2777 |
|
| 2778 |
if ( $force !== false && WFFN_Core()->admin_notifications->is_user_dismissed( get_current_user_id(), 'wizard_open' ) ) { |
| 2779 |
/** |
| 2780 |
* This flag tells us that the wizard first step is already opened |
| 2781 |
* We are using this to make sure we never redirect multiple times. |
| 2782 |
*/ |
| 2783 |
return false; |
| 2784 |
} |
| 2785 |
|
| 2786 |
if ( false !== get_option( '_bwfan_onboarding_completed', false ) && 'activated' === WFFN_Common::get_plugin_status( 'funnelkit-stripe-woo-payment-gateway/funnelkit-stripe-woo-payment-gateway.php' ) ) { |
| 2787 |
/** |
| 2788 |
* This checks if user has completed wizard from automations plugin & we have stripe plugin activated |
| 2789 |
*/ |
| 2790 |
return false; |
| 2791 |
} |
| 2792 |
|
| 2793 |
return true; |
| 2794 |
} catch ( Exception |Error $e ) { |
| 2795 |
return false; |
| 2796 |
} |
| 2797 |
} |
| 2798 |
|
| 2799 |
|
| 2800 |
/** |
| 2801 |
* Check if update is available for the plugin lite Or PRO versions |
| 2802 |
* |
| 2803 |
* @return false|mixed version number if update available, false if not available |
| 2804 |
*/ |
| 2805 |
public function is_update_available( $only_pro = false ) { |
| 2806 |
|
| 2807 |
$plugins = get_site_transient( 'update_plugins' ); |
| 2808 |
|
| 2809 |
if ( $only_pro ) { |
| 2810 |
if ( defined( 'WFFN_PRO_PLUGIN_BASENAME' ) && isset( $plugins->response ) && is_array( $plugins->response ) && isset( $plugins->response[ WFFN_PRO_PLUGIN_BASENAME ] ) ) { |
| 2811 |
return $this->compare_version( WFFN_PRO_VERSION, $plugins->response[ WFFN_PRO_PLUGIN_BASENAME ]->new_version ); |
| 2812 |
} else { |
| 2813 |
return false; |
| 2814 |
} |
| 2815 |
} elseif ( isset( $plugins->response ) && is_array( $plugins->response ) && isset( $plugins->response[ WFFN_PLUGIN_BASENAME ] ) ) { |
| 2816 |
|
| 2817 |
return $this->compare_version( WFFN_VERSION, $plugins->response[ WFFN_PLUGIN_BASENAME ]->new_version ); |
| 2818 |
} elseif ( defined( 'WFFN_PRO_PLUGIN_BASENAME' ) && isset( $plugins->response ) && is_array( $plugins->response ) && isset( $plugins->response[ WFFN_PRO_PLUGIN_BASENAME ] ) ) { |
| 2819 |
return $this->compare_version( WFFN_PRO_VERSION, $plugins->response[ WFFN_PRO_PLUGIN_BASENAME ]->new_version ); |
| 2820 |
} |
| 2821 |
|
| 2822 |
return false; |
| 2823 |
} |
| 2824 |
|
| 2825 |
/** |
| 2826 |
* Compare version |
| 2827 |
* |
| 2828 |
* @param $old_version |
| 2829 |
* @param $new_version |
| 2830 |
* |
| 2831 |
* @return false|mixed |
| 2832 |
*/ |
| 2833 |
public function compare_version( $old_version, $new_version ) { |
| 2834 |
|
| 2835 |
if ( version_compare( $new_version, $old_version, '>' ) ) { |
| 2836 |
return $new_version; |
| 2837 |
} else { |
| 2838 |
return false; |
| 2839 |
} |
| 2840 |
} |
| 2841 |
|
| 2842 |
|
| 2843 |
|
| 2844 |
|
| 2845 |
/** |
| 2846 |
* Clear wffn endpoints form cache plugins |
| 2847 |
* |
| 2848 |
* @return void |
| 2849 |
*/ |
| 2850 |
public function clear_endpoints_from_cache() { |
| 2851 |
/** Cache handling */ |
| 2852 |
if ( class_exists( 'BWF_JSON_Cache' ) && method_exists( 'BWF_JSON_Cache', 'run_json_endpoints_cache_handling' ) ) { |
| 2853 |
BWF_JSON_Cache::run_json_endpoints_cache_handling(); |
| 2854 |
} |
| 2855 |
} |
| 2856 |
|
| 2857 |
public function conditional_includes() { |
| 2858 |
|
| 2859 |
$screen = get_current_screen(); |
| 2860 |
|
| 2861 |
if ( ! $screen ) { |
| 2862 |
return; |
| 2863 |
} |
| 2864 |
|
| 2865 |
switch ( $screen->id ) { |
| 2866 |
case 'dashboard': |
| 2867 |
case 'dashboard-network': |
| 2868 |
include_once __DIR__ . '/class-wffn-admin-dashboard-widget.php'; |
| 2869 |
|
| 2870 |
break; |
| 2871 |
|
| 2872 |
case 'product': |
| 2873 |
case 'edit-product_cat': |
| 2874 |
require_once __DIR__ . '/class-fk-checkout-redirect-admin.php'; |
| 2875 |
FK_Checkout_Redirect_Admin::get_instance(); |
| 2876 |
|
| 2877 |
// Load product tab addon only on product edit screen |
| 2878 |
include_once __DIR__ . '/includes/class-wffn-product-tab-addon.php'; |
| 2879 |
WFFN_Product_Tab_Addon::get_instance(); |
| 2880 |
|
| 2881 |
break; |
| 2882 |
|
| 2883 |
} |
| 2884 |
} |
| 2885 |
|
| 2886 |
/** |
| 2887 |
* Function run on schedule for clean up data |
| 2888 |
* duplicate entry and remove subscription form conversion |
| 2889 |
* |
| 2890 |
* @return void |
| 2891 |
*/ |
| 2892 |
public function optimize_conversion_table_analytics() { |
| 2893 |
|
| 2894 |
global $wpdb; |
| 2895 |
try { |
| 2896 |
if ( ! class_exists( 'BWF_WC_Compatibility' ) ) { |
| 2897 |
return; |
| 2898 |
} |
| 2899 |
$conv_table = $wpdb->prefix . 'bwf_conversion_tracking'; |
| 2900 |
$order_table = $wpdb->prefix . 'wc_orders'; |
| 2901 |
$aero_table = $wpdb->prefix . 'wfacp_stats'; |
| 2902 |
$limit = 100; |
| 2903 |
$is_clear_schedule = true; |
| 2904 |
WFFN_Common::$start_time = time(); |
| 2905 |
|
| 2906 |
while ( true ) { |
| 2907 |
// Stop if time or memory exceeded |
| 2908 |
if ( ( WFFN_Common::time_exceeded() || WFFN_Common::memory_exceeded() ) ) { |
| 2909 |
$is_clear_schedule = false; |
| 2910 |
break; |
| 2911 |
} |
| 2912 |
|
| 2913 |
// Query Setup |
| 2914 |
if ( BWF_WC_Compatibility::is_hpos_enabled() ) { |
| 2915 |
$order_meta_table = $wpdb->prefix . 'wc_orders_meta'; |
| 2916 |
$conv_query = "SELECT conv.source as ID FROM {$conv_table} AS conv |
| 2917 |
INNER JOIN {$order_meta_table} AS om ON (conv.source = om.order_id AND om.meta_key = '_subscription_renewal') |
| 2918 |
WHERE conv.type = %s |
| 2919 |
ORDER BY conv.timestamp DESC LIMIT 0, %d";//phpcs:ignore |
| 2920 |
|
| 2921 |
} else { |
| 2922 |
$conv_query = "SELECT conv.source as ID FROM {$conv_table} AS conv |
| 2923 |
INNER JOIN {$wpdb->postmeta} AS pm ON (conv.source = pm.post_id AND pm.meta_key = '_subscription_renewal') |
| 2924 |
WHERE conv.type = %s |
| 2925 |
ORDER BY conv.timestamp DESC LIMIT 0, %d"; |
| 2926 |
} |
| 2927 |
|
| 2928 |
$get_ids = $wpdb->get_col( $wpdb->prepare( $conv_query, 2, $limit ) );//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared |
| 2929 |
|
| 2930 |
// Stop loop if no more data |
| 2931 |
if ( empty( $get_ids ) || ! is_array( $get_ids ) ) { |
| 2932 |
/* |
| 2933 |
* Attempt duplicate query |
| 2934 |
*/ |
| 2935 |
$duplicate_row = $wpdb->get_col( $wpdb->prepare( "SELECT source FROM {$conv_table} WHERE type = 2 AND source != 0 GROUP BY source HAVING COUNT(*) > %d ", 1 ) );//phpcs:ignore |
| 2936 |
|
| 2937 |
if ( ! empty( $duplicate_row ) && is_array( $duplicate_row ) ) { |
| 2938 |
$is_clear_schedule = false; |
| 2939 |
$delete_query = "DELETE FROM {$conv_table} WHERE id IN ( SELECT id FROM ( |
| 2940 |
SELECT id FROM {$conv_table} WHERE source IN ( |
| 2941 |
SELECT source FROM {$conv_table} WHERE type = 2 AND source != 0 |
| 2942 |
GROUP BY source HAVING COUNT(*) > 1 ) AND id NOT IN ( |
| 2943 |
SELECT MAX(id) FROM {$conv_table} GROUP BY source ) LIMIT 100 ) AS subquery )"; |
| 2944 |
$wpdb->query( $delete_query ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared |
| 2945 |
WFFN_Core()->logger->log( 'Clear duplicate rows.', 'wffn_ay', true ); |
| 2946 |
continue; |
| 2947 |
|
| 2948 |
} else { |
| 2949 |
WFFN_Core()->logger->log( 'No more data to clear from conversion tracking.', 'wffn_ay', true ); |
| 2950 |
break; |
| 2951 |
} |
| 2952 |
} |
| 2953 |
|
| 2954 |
$is_clear_schedule = false; |
| 2955 |
$delete_query = "DELETE FROM {$conv_table} WHERE type = %d AND source IN (" . implode( ',', array_map( 'intval', $get_ids ) ) . ')'; |
| 2956 |
$wpdb->query( $wpdb->prepare( $delete_query, 2 ) );//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared |
| 2957 |
|
| 2958 |
if ( ! empty( $wpdb->last_error ) ) { |
| 2959 |
$is_clear_schedule = true; |
| 2960 |
WFFN_Core()->logger->log( 'Conversion tracking cleanup last error: ' . $wpdb->last_error, 'wffn_ay', true ); |
| 2961 |
} |
| 2962 |
|
| 2963 |
$update_query = "UPDATE {$conv_table} AS conv LEFT JOIN {$aero_table} AS stats ON conv.source = stats.order_id SET conv.checkout_total = stats.total_revenue |
| 2964 |
WHERE 1=1 AND conv.funnel_id != 0 |
| 2965 |
AND conv.checkout_total = 0 |
| 2966 |
AND stats.total_revenue != 0 |
| 2967 |
AND conv.type = %d |
| 2968 |
AND conv.timestamp > '2025-01-01 00:00:00'"; |
| 2969 |
|
| 2970 |
$wpdb->query( $wpdb->prepare( $update_query, 2 ) );//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared |
| 2971 |
|
| 2972 |
if ( ! empty( $wpdb->last_error ) ) { |
| 2973 |
$is_clear_schedule = true; |
| 2974 |
WFFN_Core()->logger->log( 'Conversion tracking update last error: ' . $wpdb->last_error, 'wffn_ay', true ); |
| 2975 |
} |
| 2976 |
} |
| 2977 |
|
| 2978 |
if ( ( WFFN_Common::time_exceeded() || WFFN_Common::memory_exceeded() ) ) { |
| 2979 |
return; |
| 2980 |
} |
| 2981 |
|
| 2982 |
/** |
| 2983 |
* Remove all subscription order from aero |
| 2984 |
*/ |
| 2985 |
$aero_table = $wpdb->prefix . 'wfacp_stats'; |
| 2986 |
|
| 2987 |
if ( BWF_WC_Compatibility::is_hpos_enabled() ) { |
| 2988 |
$aero_query = "SELECT ch.order_id as ID FROM {$aero_table} AS ch |
| 2989 |
INNER JOIN {$order_table} AS ot ON (ch.order_id = ot.id ) |
| 2990 |
WHERE ot.type = %s |
| 2991 |
ORDER BY ch.date DESC LIMIT 0, %d"; |
| 2992 |
|
| 2993 |
} else { |
| 2994 |
$aero_query = "SELECT ch.order_id as ID FROM {$aero_table} AS ch |
| 2995 |
INNER JOIN {$wpdb->posts} AS p ON (ch.order_id = p.ID) |
| 2996 |
WHERE p.post_type = %s |
| 2997 |
ORDER BY ch.date DESC LIMIT 0, %d"; |
| 2998 |
} |
| 2999 |
|
| 3000 |
$get_aero_ids = $wpdb->get_col( $wpdb->prepare( $aero_query, 'shop_subscription', $limit ) );//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared |
| 3001 |
if ( is_array( $get_aero_ids ) && count( $get_aero_ids ) > 0 ) { |
| 3002 |
$is_clear_schedule = false; |
| 3003 |
$wpdb->query( "DELETE FROM {$aero_table} WHERE order_id IN (" . implode( ',', $get_aero_ids ) . " )" );//phpcs:ignore |
| 3004 |
if ( ! empty( $wpdb->last_error ) ) { |
| 3005 |
$is_clear_schedule = true; |
| 3006 |
WFFN_Core()->logger->log( 'Conversion aero tracking cleanup last error: ' . $wpdb->last_error, 'wffn_ay', true ); |
| 3007 |
} |
| 3008 |
} |
| 3009 |
|
| 3010 |
/** |
| 3011 |
* Remove duplicate entry |
| 3012 |
*/ |
| 3013 |
$duplicate_query = "SELECT order_id as ID FROM {$aero_table} |
| 3014 |
WHERE order_id IN ( SELECT order_id FROM {$aero_table} |
| 3015 |
GROUP BY order_id |
| 3016 |
HAVING COUNT(order_id) > %d |
| 3017 |
) |
| 3018 |
ORDER BY order_id, ID LIMIT 0, %d"; |
| 3019 |
|
| 3020 |
$duplicate_aero_ids = $wpdb->get_col( $wpdb->prepare( $duplicate_query, 1, $limit ) );//phpcs:ignore |
| 3021 |
|
| 3022 |
if ( is_array( $duplicate_aero_ids ) && count( $duplicate_aero_ids ) > 0 ) { |
| 3023 |
$is_clear_schedule = false; |
| 3024 |
$delete_query = "DELETE FROM {$aero_table} WHERE ID IN ( |
| 3025 |
SELECT ID FROM ( |
| 3026 |
SELECT ID |
| 3027 |
FROM {$aero_table} t1 |
| 3028 |
WHERE ID NOT IN ( |
| 3029 |
SELECT MAX(ID) FROM {$aero_table} t2 GROUP BY order_id |
| 3030 |
) |
| 3031 |
AND order_id IN ( |
| 3032 |
SELECT order_id FROM {$aero_table} GROUP BY order_id HAVING COUNT(order_id) > %d |
| 3033 |
) |
| 3034 |
ORDER BY ID DESC |
| 3035 |
LIMIT %d |
| 3036 |
) AS subquery |
| 3037 |
)"; |
| 3038 |
$wpdb->query( $wpdb->prepare( $delete_query, 1, $limit ) );//phpcs:ignore |
| 3039 |
if ( ! empty( $wpdb->last_error ) ) { |
| 3040 |
$is_clear_schedule = true; |
| 3041 |
WFFN_Core()->logger->log( 'Conversion duplicate tracking cleanup last error: ' . $wpdb->last_error, 'wffn_ay', true ); |
| 3042 |
} |
| 3043 |
} |
| 3044 |
|
| 3045 |
/** |
| 3046 |
* Remove all subscription order from bump |
| 3047 |
*/ |
| 3048 |
if ( class_exists( 'WFOB_Core' ) ) { |
| 3049 |
$bump_table = $wpdb->prefix . 'wfob_stats'; |
| 3050 |
if ( BWF_WC_Compatibility::is_hpos_enabled() ) { |
| 3051 |
$bump_query = "SELECT ch.oid as ID FROM {$bump_table} AS ch |
| 3052 |
INNER JOIN {$order_table} AS ot ON (ch.oid = ot.id ) |
| 3053 |
WHERE ot.type = %s |
| 3054 |
ORDER BY ch.date DESC LIMIT 0, %d"; |
| 3055 |
|
| 3056 |
} else { |
| 3057 |
$bump_query = "SELECT ch.oid as ID FROM {$bump_table} AS ch |
| 3058 |
INNER JOIN {$wpdb->posts} AS p ON (ch.oid = p.ID) |
| 3059 |
WHERE p.post_type = %s |
| 3060 |
ORDER BY ch.date DESC LIMIT 0, %d"; |
| 3061 |
} |
| 3062 |
|
| 3063 |
$get_bump_ids = $wpdb->get_col( $wpdb->prepare( $bump_query, 'shop_subscription', $limit ) );//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared |
| 3064 |
if ( is_array( $get_bump_ids ) && count( $get_bump_ids ) > 0 ) { |
| 3065 |
$is_clear_schedule = false; |
| 3066 |
$wpdb->query( "DELETE FROM {$bump_table} WHERE oid IN (" . implode( ',', $get_bump_ids ) . " )" );//phpcs:ignore |
| 3067 |
if ( ! empty( $wpdb->last_error ) ) { |
| 3068 |
$is_clear_schedule = true; |
| 3069 |
WFFN_Core()->logger->log( 'Conversion bump tracking cleanup last error: ' . $wpdb->last_error, 'wffn_ay', true ); |
| 3070 |
} |
| 3071 |
} |
| 3072 |
} |
| 3073 |
|
| 3074 |
// If no remaining rows, schedule a single action to remove the recurring action |
| 3075 |
if ( $is_clear_schedule ) { |
| 3076 |
wp_schedule_single_event( time(), 'fk_remove_optimize_conversion_table_schedule' ); |
| 3077 |
} |
| 3078 |
} catch ( Exception |Error $e ) { |
| 3079 |
// Schedule a single action to delete the recurring action |
| 3080 |
wp_schedule_single_event( time(), 'fk_remove_optimize_conversion_table_schedule' ); |
| 3081 |
WFFN_Core()->logger->log( 'Exception occurred in : ' . __FUNCTION__ . $e->getMessage(), 'wffn_ay', true ); |
| 3082 |
|
| 3083 |
} |
| 3084 |
} |
| 3085 |
|
| 3086 |
public function remove_optimize_conversion_table_schedule() { |
| 3087 |
// Clear the scheduled action |
| 3088 |
wp_clear_scheduled_hook( 'fk_optimize_conversion_table_analytics' ); |
| 3089 |
WFFN_Core()->logger->log( 'Recurring action "fk_optimize_conversion_table_analytics" cleared : ' . __FUNCTION__, 'wffn_ay', true ); |
| 3090 |
} |
| 3091 |
|
| 3092 |
/** |
| 3093 |
* Runs the email notifications. |
| 3094 |
* |
| 3095 |
* @return void |
| 3096 |
*/ |
| 3097 |
public function run_notifications() { |
| 3098 |
WFFN_Email_Notification::run_notifications(); |
| 3099 |
} |
| 3100 |
|
| 3101 |
/** |
| 3102 |
* Runs the email notifications. |
| 3103 |
* |
| 3104 |
* @return void |
| 3105 |
*/ |
| 3106 |
public function maybe_setup_notification_schedule() { |
| 3107 |
WFFN_Email_Notification::maybe_setup_notification_schedule(); |
| 3108 |
} |
| 3109 |
|
| 3110 |
/** |
| 3111 |
* Tests the email notification in the admin area. |
| 3112 |
* |
| 3113 |
* @return void |
| 3114 |
*/ |
| 3115 |
public function test_notification_admin() { |
| 3116 |
|
| 3117 |
WFFN_Email_Notification::test_notification_admin(); |
| 3118 |
} |
| 3119 |
|
| 3120 |
/** |
| 3121 |
* save settings for email notification |
| 3122 |
**/ |
| 3123 |
public function save_settings_for_email_notification( $settings ) { |
| 3124 |
WFFN_Email_Notification::save_settings( $settings ); |
| 3125 |
} |
| 3126 |
|
| 3127 |
|
| 3128 |
/** |
| 3129 |
* Check if language support is enabled |
| 3130 |
* |
| 3131 |
* @return bool |
| 3132 |
*/ |
| 3133 |
public function is_language_support_enabled() { |
| 3134 |
return '' !== WFFN_Plugin_Compatibilities::get_language_compatible_plugin(); |
| 3135 |
} |
| 3136 |
|
| 3137 |
/** |
| 3138 |
* Whether the installed Funnel Builder Pro is older than the build this |
| 3139 |
* release pairs with (WFFN_MIN_PRO_VERSION). |
| 3140 |
* |
| 3141 |
* @return bool |
| 3142 |
*/ |
| 3143 |
public function is_outdated() { |
| 3144 |
if ( ! defined( 'WFFN_PRO_VERSION' ) || ! defined( 'WFFN_MIN_PRO_VERSION' ) ) { |
| 3145 |
return false; |
| 3146 |
} |
| 3147 |
|
| 3148 |
/** Source checkouts carry an unstamped build placeholder, not a comparable version. */ |
| 3149 |
if ( false !== strpos( WFFN_PRO_VERSION, '{{{' ) ) { |
| 3150 |
return false; |
| 3151 |
} |
| 3152 |
|
| 3153 |
return version_compare( WFFN_PRO_VERSION, WFFN_MIN_PRO_VERSION, '<' ); |
| 3154 |
} |
| 3155 |
|
| 3156 |
/** |
| 3157 |
* Name of the installed premium companion plugin the pairing applies to. |
| 3158 |
* |
| 3159 |
* @return string |
| 3160 |
*/ |
| 3161 |
public function get_companion_name() { |
| 3162 |
return defined( 'WFFN_BASIC_FILE' ) |
| 3163 |
? __( 'FunnelKit Funnel Builder Basic', 'funnel-builder' ) |
| 3164 |
: __( 'FunnelKit Funnel Builder Pro', 'funnel-builder' ); |
| 3165 |
} |
| 3166 |
|
| 3167 |
/** |
| 3168 |
* Companion plugin name without the brand prefix, for tight spaces. |
| 3169 |
* |
| 3170 |
* @return string |
| 3171 |
*/ |
| 3172 |
public function get_companion_short_name() { |
| 3173 |
return defined( 'WFFN_BASIC_FILE' ) |
| 3174 |
? __( 'Funnel Builder Basic', 'funnel-builder' ) |
| 3175 |
: __( 'Funnel Builder Pro', 'funnel-builder' ); |
| 3176 |
} |
| 3177 |
|
| 3178 |
/** |
| 3179 |
* Adds a pairing-requirement row under the free Funnel Builder plugin on |
| 3180 |
* the plugins page. A separate row on its own hook — the core update rows |
| 3181 |
* (both plugins') stay untouched. Registered on admin_init so it also |
| 3182 |
* covers the AJAX search rerender, where load-plugins.php never fires. |
| 3183 |
* |
| 3184 |
* @return void |
| 3185 |
*/ |
| 3186 |
public function maybe_register_update_row_message() { |
| 3187 |
if ( ! $this->is_outdated() || ! defined( 'WFFN_PLUGIN_BASENAME' ) ) { |
| 3188 |
return; |
| 3189 |
} |
| 3190 |
|
| 3191 |
add_action( 'in_plugin_update_message-' . WFFN_PLUGIN_BASENAME, array( $this, 'render_in_core_update_row' ) ); |
| 3192 |
add_action( 'after_plugin_row_' . WFFN_PLUGIN_BASENAME, array( $this, 'render_update_row' ), 11, 2 ); |
| 3193 |
} |
| 3194 |
|
| 3195 |
/** |
| 3196 |
* Appended inside the core "There is a new version…" row when the free |
| 3197 |
* plugin itself has an update on offer. |
| 3198 |
* |
| 3199 |
* @return void |
| 3200 |
*/ |
| 3201 |
public function render_in_core_update_row() { |
| 3202 |
$this->render_pairing_warning_block( true ); |
| 3203 |
} |
| 3204 |
|
| 3205 |
/** |
| 3206 |
* The warning block shared by both plugins-page placements. |
| 3207 |
* |
| 3208 |
* @param bool $with_separator Whether a core message precedes the block. |
| 3209 |
* |
| 3210 |
* @return void |
| 3211 |
*/ |
| 3212 |
private function render_pairing_warning_block( $with_separator ) { |
| 3213 |
$update_link = ''; |
| 3214 |
if ( defined( 'WFFN_PRO_FILE' ) && current_user_can( 'update_plugins' ) ) { |
| 3215 |
$pro_file = plugin_basename( WFFN_PRO_FILE ); |
| 3216 |
$updates = get_site_transient( 'update_plugins' ); |
| 3217 |
if ( ! empty( $updates->response[ $pro_file ]->package ) ) { |
| 3218 |
$update_link = sprintf( |
| 3219 |
' <a href="%1$s" aria-label="%2$s">%3$s</a>', |
| 3220 |
esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $pro_file, 'upgrade-plugin_' . $pro_file ) ), |
| 3221 |
esc_attr( sprintf( /* translators: %s: premium plugin name */ __( 'Update %s now', 'funnel-builder' ), $this->get_companion_name() ) ), |
| 3222 |
esc_html__( 'Update now', 'funnel-builder' ) |
| 3223 |
); |
| 3224 |
} |
| 3225 |
} |
| 3226 |
|
| 3227 |
if ( $with_separator ) { |
| 3228 |
?> |
| 3229 |
<hr class="wffn-pairing-warning__separator" /> |
| 3230 |
<?php |
| 3231 |
} |
| 3232 |
?> |
| 3233 |
<div class="wffn-pairing-warning"> |
| 3234 |
<div class="wffn-pairing-warning__icon"> |
| 3235 |
<span class="dashicons dashicons-info" aria-hidden="true"></span> |
| 3236 |
</div> |
| 3237 |
<div> |
| 3238 |
<div class="wffn-pairing-warning__title"> |
| 3239 |
<?php |
| 3240 |
printf( /* translators: %s: premium plugin name */ |
| 3241 |
esc_html__( 'Update Required: %s', 'funnel-builder' ), |
| 3242 |
esc_html( $this->get_companion_name() ) |
| 3243 |
); |
| 3244 |
?> |
| 3245 |
</div> |
| 3246 |
<div class="wffn-pairing-warning__message"> |
| 3247 |
<?php |
| 3248 |
printf( /* translators: 1: premium plugin name, 2: required version, 3: installed version */ |
| 3249 |
esc_html__( 'This version of Funnel Builder pairs with %1$s %2$s or newer, but %3$s is installed.', 'funnel-builder' ), |
| 3250 |
esc_html( $this->get_companion_name() ), |
| 3251 |
esc_html( WFFN_MIN_PRO_VERSION ), |
| 3252 |
esc_html( WFFN_PRO_VERSION ) |
| 3253 |
); |
| 3254 |
echo wp_kses_post( $update_link ); |
| 3255 |
?> |
| 3256 |
</div> |
| 3257 |
</div> |
| 3258 |
</div> |
| 3259 |
<style> |
| 3260 |
/* Inline so the block also survives the AJAX search rerender of the list table. */ |
| 3261 |
.wffn-pairing-warning { |
| 3262 |
display: flex; |
| 3263 |
max-width: 1000px; |
| 3264 |
margin-block-end: 5px; |
| 3265 |
} |
| 3266 |
|
| 3267 |
.wffn-pairing-warning__separator { |
| 3268 |
margin: 15px -12px; |
| 3269 |
border: 1px solid #ffb900; |
| 3270 |
border-bottom: none; |
| 3271 |
} |
| 3272 |
|
| 3273 |
.wffn-pairing-warning__icon { |
| 3274 |
margin-inline-end: 9px; |
| 3275 |
margin-inline-start: 2px; |
| 3276 |
color: #f56e28; |
| 3277 |
} |
| 3278 |
|
| 3279 |
.wffn-pairing-warning__icon .dashicons { |
| 3280 |
font-size: 17px; |
| 3281 |
width: 17px; |
| 3282 |
height: 17px; |
| 3283 |
vertical-align: text-top; |
| 3284 |
} |
| 3285 |
|
| 3286 |
.wffn-pairing-warning__title { |
| 3287 |
font-weight: 600; |
| 3288 |
margin-block-end: 10px; |
| 3289 |
} |
| 3290 |
|
| 3291 |
.wffn-pairing-warning ~ p { |
| 3292 |
display: none; |
| 3293 |
} |
| 3294 |
</style> |
| 3295 |
<?php |
| 3296 |
} |
| 3297 |
|
| 3298 |
/** |
| 3299 |
* Same markup core uses for its update row, so styling and the shiny |
| 3300 |
* (AJAX) update flow keep working. |
| 3301 |
* |
| 3302 |
* @param string $file Plugin basename. |
| 3303 |
* @param array $plugin_data Plugin headers. |
| 3304 |
* |
| 3305 |
* @return void |
| 3306 |
*/ |
| 3307 |
public function render_update_row( $file, $plugin_data ) { |
| 3308 |
/** The core update row is present instead; the block renders inside it. */ |
| 3309 |
$updates = get_site_transient( 'update_plugins' ); |
| 3310 |
if ( isset( $updates->response[ $file ] ) ) { |
| 3311 |
return; |
| 3312 |
} |
| 3313 |
|
| 3314 |
$wp_list_table = _get_list_table( 'WP_Plugins_List_Table', array( 'screen' => get_current_screen() ) ); |
| 3315 |
$active_class = is_plugin_active( $file ) ? ' active' : ''; |
| 3316 |
?> |
| 3317 |
<tr class="plugin-update-tr wffn-pro-pairing-tr<?php echo esc_attr( $active_class ); ?>" id="wffn-pro-pairing-update" data-plugin="<?php echo esc_attr( $file ); ?>"> |
| 3318 |
<td colspan="<?php echo esc_attr( $wp_list_table->get_column_count() ); ?>" class="plugin-update colspanchange"> |
| 3319 |
<div class="update-message notice inline notice-warning notice-alt"> |
| 3320 |
<?php $this->render_pairing_warning_block( false ); ?> |
| 3321 |
</div> |
| 3322 |
</td> |
| 3323 |
</tr> |
| 3324 |
<?php |
| 3325 |
} |
| 3326 |
|
| 3327 |
/** |
| 3328 |
* Site-wide admin notice while Pro is outdated. Skipped on the FunnelKit |
| 3329 |
* app screens, where the full-page prompt already covers it. |
| 3330 |
* |
| 3331 |
* @return void |
| 3332 |
*/ |
| 3333 |
public function maybe_render_sitewide_notice() { |
| 3334 |
if ( ! $this->is_outdated() ) { |
| 3335 |
return; |
| 3336 |
} |
| 3337 |
|
| 3338 |
if ( class_exists( 'WFFN_Role_Capability' ) && ! WFFN_Role_Capability::get_instance()->user_access( 'menu', 'read' ) ) { |
| 3339 |
return; |
| 3340 |
} |
| 3341 |
|
| 3342 |
if ( $this->is_wffn_flex_page( 'all' ) ) { |
| 3343 |
return; |
| 3344 |
} |
| 3345 |
?> |
| 3346 |
<div class="notice notice-warning"> |
| 3347 |
<p> |
| 3348 |
<strong> |
| 3349 |
<?php |
| 3350 |
printf( /* translators: %s: premium plugin name */ |
| 3351 |
esc_html__( 'Update %s', 'funnel-builder' ), |
| 3352 |
esc_html( $this->get_companion_name() ) |
| 3353 |
); |
| 3354 |
?> |
| 3355 |
</strong> |
| 3356 |
</p> |
| 3357 |
<p> |
| 3358 |
<?php |
| 3359 |
printf( /* translators: 1: installed Funnel Builder version, 2: premium plugin name without brand prefix, 3: required version */ |
| 3360 |
esc_html__( 'Funnel Builder %1$s pairs with %2$s version %3$s or higher.', 'funnel-builder' ), |
| 3361 |
esc_html( WFFN_VERSION ), |
| 3362 |
esc_html( $this->get_companion_short_name() ), |
| 3363 |
esc_html( WFFN_MIN_PRO_VERSION ) |
| 3364 |
); |
| 3365 |
echo ' '; |
| 3366 |
esc_html_e( 'Some Pro features may not work correctly until Pro is updated. Updating takes about a minute.', 'funnel-builder' ); |
| 3367 |
?> |
| 3368 |
</p> |
| 3369 |
<p> |
| 3370 |
<a class="button button-primary" href="<?php echo esc_url( self_admin_url( 'plugins.php?s=FunnelKit+Funnel+Builder' ) ); ?>"><?php esc_html_e( 'Update Now', 'funnel-builder' ); ?></a> |
| 3371 |
</p> |
| 3372 |
</div> |
| 3373 |
<?php |
| 3374 |
} |
| 3375 |
|
| 3376 |
/** |
| 3377 |
* Marks the FunnelKit toolbar menu while Pro is outdated: a badge on the |
| 3378 |
* top-level node and an update link as the only dropdown entry. |
| 3379 |
* |
| 3380 |
* @param WP_Admin_Bar $wp_admin_bar |
| 3381 |
* |
| 3382 |
* @return void |
| 3383 |
*/ |
| 3384 |
public function maybe_add_admin_bar_state( WP_Admin_Bar $wp_admin_bar ) { |
| 3385 |
if ( ! $this->is_outdated() ) { |
| 3386 |
return; |
| 3387 |
} |
| 3388 |
|
| 3389 |
$node = $wp_admin_bar->get_node( 'wffn_funnel' ); |
| 3390 |
if ( empty( $node ) ) { |
| 3391 |
return; |
| 3392 |
} |
| 3393 |
|
| 3394 |
$wp_admin_bar->add_node( |
| 3395 |
array( |
| 3396 |
'id' => 'wffn_funnel', |
| 3397 |
'title' => $node->title . '<span class="wffn-ab-update-badge" aria-label="' . esc_attr__( 'Update Required', 'funnel-builder' ) . '"><span class="wffn-ab-update-badge__dot"></span></span>', |
| 3398 |
'href' => self_admin_url( 'plugins.php?s=FunnelKit+Funnel+Builder' ), |
| 3399 |
) |
| 3400 |
); |
| 3401 |
|
| 3402 |
/** The admin app is replaced by the update prompt, so its routes lead nowhere. */ |
| 3403 |
$dead_parents = array( 'wffn_funnel' ); |
| 3404 |
do { |
| 3405 |
$removed = false; |
| 3406 |
foreach ( (array) $wp_admin_bar->get_nodes() as $sub_node ) { |
| 3407 |
if ( 'wffn_funnel' !== $sub_node->id && in_array( $sub_node->parent, $dead_parents, true ) ) { |
| 3408 |
$wp_admin_bar->remove_node( $sub_node->id ); |
| 3409 |
$dead_parents[] = $sub_node->id; |
| 3410 |
$removed = true; |
| 3411 |
} |
| 3412 |
} |
| 3413 |
} while ( $removed ); |
| 3414 |
|
| 3415 |
$wp_admin_bar->add_menu( |
| 3416 |
array( |
| 3417 |
'id' => 'wffn_funnel_update_required', |
| 3418 |
'parent' => 'wffn_funnel', |
| 3419 |
'title' => '<span class="wffn-ab-update-title">' . sprintf( /* translators: %s: premium plugin name without brand prefix */ esc_html__( 'Update %s', 'funnel-builder' ), esc_html( $this->get_companion_short_name() ) ) . '</span><span class="wffn-ab-update-sub">' . sprintf( /* translators: %s: required version ("+" stands for "or higher") */ esc_html__( 'Requires %s+', 'funnel-builder' ), esc_html( WFFN_MIN_PRO_VERSION ) ) . '</span>', |
| 3420 |
'href' => self_admin_url( 'plugins.php?s=FunnelKit+Funnel+Builder' ), |
| 3421 |
) |
| 3422 |
); |
| 3423 |
?> |
| 3424 |
<style type="text/css"> |
| 3425 |
#wp-admin-bar-wffn_funnel .wffn-ab-update-badge { |
| 3426 |
display: inline-flex; |
| 3427 |
align-items: center; |
| 3428 |
justify-content: center; |
| 3429 |
width: 16px; |
| 3430 |
height: 16px; |
| 3431 |
margin-left: 6px; |
| 3432 |
border-radius: 50%; |
| 3433 |
background: #f2c14b2e; |
| 3434 |
vertical-align: middle; |
| 3435 |
} |
| 3436 |
|
| 3437 |
#wp-admin-bar-wffn_funnel .wffn-ab-update-badge__dot { |
| 3438 |
width: 8px; |
| 3439 |
height: 8px; |
| 3440 |
border-radius: 50%; |
| 3441 |
background: #f2c14b; |
| 3442 |
} |
| 3443 |
|
| 3444 |
#wpadminbar ul#wp-admin-bar-wffn_funnel-default li#wp-admin-bar-wffn_funnel_update_required a.ab-item { |
| 3445 |
height: auto; |
| 3446 |
padding-top: 4px; |
| 3447 |
padding-bottom: 6px; |
| 3448 |
line-height: 18px; |
| 3449 |
} |
| 3450 |
|
| 3451 |
#wpadminbar ul#wp-admin-bar-wffn_funnel-default .wffn-ab-update-title { |
| 3452 |
display: block; |
| 3453 |
font-weight: 600; |
| 3454 |
line-height: 18px; |
| 3455 |
color: #e2b644; |
| 3456 |
} |
| 3457 |
|
| 3458 |
#wpadminbar ul#wp-admin-bar-wffn_funnel-default .wffn-ab-update-sub { |
| 3459 |
display: block; |
| 3460 |
margin-top: 2px; |
| 3461 |
line-height: 18px; |
| 3462 |
color: #a7aaad; |
| 3463 |
} |
| 3464 |
</style> |
| 3465 |
<?php |
| 3466 |
} |
| 3467 |
|
| 3468 |
/** |
| 3469 |
* Adds the update-required entry to the in-app notifications feed while |
| 3470 |
* Pro is outdated. |
| 3471 |
* |
| 3472 |
* @param array $notifs |
| 3473 |
* |
| 3474 |
* @return array |
| 3475 |
*/ |
| 3476 |
public function maybe_have_outdated_notif( $notifs ) { |
| 3477 |
if ( $this->is_outdated() ) { |
| 3478 |
if ( false !== $this->is_update_available( true ) ) { |
| 3479 |
$notifs[] = array( |
| 3480 |
'key' => 'fb_required_update_3_16', |
| 3481 |
'content' => '<div class="bwf-notifications-message current"> |
| 3482 |
<h3 class="bwf-notifications-title">' . sprintf( __( 'Alert! Your %s version is not in sync with the current lite version.', 'funnel-builder' ), $this->get_companion_name() ) . '</h3> |
| 3483 |
<p class="bwf-notifications-content">' . __( 'Due to some technical changes in how admin application is loaded, the free version has fallen out of sync with the premium version. Consider updating the premium version.', 'funnel-builder' ) . '</p> |
| 3484 |
</div>', |
| 3485 |
'not_dismissible' => true, |
| 3486 |
'customButtons' => array( |
| 3487 |
array( |
| 3488 |
'label' => __( 'Update Now', 'funnel-builder' ), |
| 3489 |
'href' => admin_url( 'plugins.php?s=FunnelKit+Funnel+Builder' ), |
| 3490 |
'className' => 'is-primary', |
| 3491 |
'target' => '__blank', |
| 3492 |
), |
| 3493 |
|
| 3494 |
), |
| 3495 |
'index' => 9, |
| 3496 |
|
| 3497 |
); |
| 3498 |
} else { |
| 3499 |
$url = 'https://myaccount.funnelkit.com/'; |
| 3500 |
$deactivate_url = ''; |
| 3501 |
if ( defined( 'WFFN_PRO_FILE' ) ) { |
| 3502 |
$pro_basename = plugin_basename( WFFN_PRO_FILE ); |
| 3503 |
$deactivate_url = wp_nonce_url( self_admin_url( 'plugins.php?action=deactivate&plugin=' . rawurlencode( $pro_basename ) ), 'deactivate-plugin_' . $pro_basename ); |
| 3504 |
} |
| 3505 |
$notifs[] = array( |
| 3506 |
'key' => 'fb_required_update_3_16', |
| 3507 |
'content' => '<div class="bwf-notifications-message current"> |
| 3508 |
<h3 class="bwf-notifications-title">' . sprintf( __( 'Alert! Your %s version is not in sync with the current lite version.', 'funnel-builder' ), $this->get_companion_name() ) . '</h3> |
| 3509 |
<p class="bwf-notifications-content">' . __( 'Due to some technical changes in how admin application is loaded, the free version has fallen out of sync with the premium version. We recommend logging into the Funnelkit accounts and downloading the latest version.', 'funnel-builder' ) . '</p> |
| 3510 |
</div>', |
| 3511 |
'not_dismissible' => true, |
| 3512 |
'customButtons' => array( |
| 3513 |
array( |
| 3514 |
'label' => __( 'Get the latest version', 'funnel-builder' ), |
| 3515 |
'href' => $url, |
| 3516 |
'className' => 'is-primary', |
| 3517 |
'target' => '__blank', |
| 3518 |
), |
| 3519 |
array( |
| 3520 |
'label' => __( 'Deactivate Funnel Builder Pro', 'funnel-builder' ), |
| 3521 |
'href' => $deactivate_url, |
| 3522 |
'target' => '__blank', |
| 3523 |
), |
| 3524 |
), |
| 3525 |
'index' => 9, |
| 3526 |
|
| 3527 |
); |
| 3528 |
} |
| 3529 |
} |
| 3530 |
|
| 3531 |
return $notifs; |
| 3532 |
} |
| 3533 |
} |
| 3534 |
|
| 3535 |
if ( class_exists( 'WFFN_Core' ) ) { |
| 3536 |
WFFN_Core::register( 'admin', 'WFFN_Admin' ); |
| 3537 |
} |
| 3538 |
} |
| 3539 |
|