| @@ -42,8 +42,23 @@ | ||
| 42 | 42 | */ |
| 43 | 43 | const WPFNL_FEED_OPTION_KEY = 'wpfnl_remote_info_feed_data'; |
| 44 | 44 | |
| 45 | 45 | /** |
| 46 | + * Plugin file of the free Mail Mint plugin, as wordpress.org ships it. | |
| 47 | + */ | |
| 48 | + const MAIL_MINT_BASENAME = 'mail-mint/mail-mint.php'; | |
| 49 | + | |
| 50 | + /** | |
| 51 | + * Plugin file of Mail Mint Pro. | |
| 52 | + */ | |
| 53 | + const MAIL_MINT_PRO_BASENAME = 'mail-mint-pro/mail-mint-pro.php'; | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * Plugin file of WPFunnels Pro. | |
| 57 | + */ | |
| 58 | + const WPFNL_PRO_BASENAME = 'wpfunnels-pro/wpfnl-pro.php'; | |
| 59 | + | |
| 60 | + /** | |
| 46 | 61 | * The ID of this plugin. |
| 47 | 62 | * |
| 48 | 63 | * @since 1.0.0 |
| 49 | 64 | * @access private |
| @@ -64,17 +79,20 @@ | ||
| 64 | 79 | private $dependency_plugins; |
| 65 | 80 | |
| 66 | 81 | |
| 67 | 82 | /** |
| 68 | - * Page hooks | |
| 83 | + * Screens still rendered by the legacy PHP/Vue admin, which is what the | |
| 84 | + * legacy stylesheet and bundle are written against. | |
| 69 | 85 | * |
| 86 | + * NOTE: 'toplevel_page_wpfunnels' is deliberately absent. That screen is the | |
| 87 | + * React admin, which ships its own namespaced bundle and must not inherit | |
| 88 | + * the legacy admin CSS. The funnel list, its trash tab and the Templates | |
| 89 | + * page are routes of that same app now, so their hooks are gone from here | |
| 90 | + * too. | |
| 91 | + * | |
| 70 | 92 | * @var array |
| 71 | 93 | */ |
| 72 | 94 | private $page_hooks = [ |
| 73 | - 'toplevel_page_wpf_templates', | |
| 74 | - 'toplevel_page_wpfunnels', | |
| 75 | - 'wpfunnels_page_wpf_templates', | |
| 76 | - 'wpfunnels_page_trash_funnels', | |
| 77 | 95 | 'wpfunnels_page_wpf_feature_comparison', |
| 78 | 96 | 'wpfunnels_page_wp_funnel_settings', |
| 79 | 97 | 'wpfunnels_page_wpfnl_settings', |
| 80 | 98 | 'wpfunnels_page_wp_global_store', |
| @@ -79,9 +97,8 @@ | ||
| 79 | 97 | 'wpfunnels_page_wpfnl_settings', |
| 80 | 98 | 'wpfunnels_page_wp_global_store', |
| 81 | 99 | 'wpfunnels_page_edit_funnel', |
| 82 | 100 | 'wpfunnels_page_create_funnel', |
| 83 | - 'wpfunnels_page_wpfnl_settings', | |
| 84 | 101 | 'wpfunnels_page_wpfnl_automations', |
| 85 | 102 | 'wpfunnels_page_wpfnl_addons', |
| 86 | 103 | 'wpfunnels_page_store_checkout', |
| 87 | 104 | 'wpfunnels_page_wpfunnels_integrations', |
| @@ -86,10 +103,8 @@ | ||
| 86 | 103 | 'wpfunnels_page_store_checkout', |
| 87 | 104 | 'wpfunnels_page_wpfunnels_integrations', |
| 88 | 105 | 'wpfunnels_page_wpf-license', |
| 89 | 106 | 'wpfunnels_page_email-builder', |
| 90 | - 'wpfunnels_page_wp_funnels', | |
| 91 | - 'wpfunnels_page_wpf_templates' | |
| 92 | 107 | ]; |
| 93 | 108 | |
| 94 | 109 | /** |
| 95 | 110 | * Initialize the class and set its properties. |
| @@ -840,29 +855,566 @@ | ||
| 840 | 855 | if ('wpfunnels_page_email-builder' === $hook) { |
| 841 | 856 | wp_enqueue_style($this->plugin_name . '-email-builder', plugin_dir_url(__FILE__) . 'assets/dist/email-builder/email-builder.css', [], $this->version, 'all'); |
| 842 | 857 | } |
| 843 | 858 | |
| 844 | - if ('wpfunnels_page_' . WPFNL_AI_FUNNEL_PAGE_SLUG === $hook) { | |
| 845 | - wp_enqueue_style('wpfnl-admin-app', plugin_dir_url(__FILE__) . 'assets/css/admin-app.css', [], $this->version, 'all'); | |
| 859 | + if (in_array($hook, $this->page_hooks)) { | |
| 860 | + wp_enqueue_style('wp-color-picker'); | |
| 861 | + wp_enqueue_style($this->plugin_name . '-jquery-ui', plugin_dir_url(__FILE__) . 'assets/css/jquery-ui.min.css', [], $this->version, 'all'); | |
| 862 | + | |
| 846 | 863 | wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'assets/css/wpfnl-admin.css', [], $this->version, 'all'); |
| 864 | + | |
| 865 | + do_action('wpfunnels_after_styles_loaded'); | |
| 847 | 866 | } |
| 867 | + } | |
| 848 | 868 | |
| 849 | - if ($this->is_current_page('wpfunnels')) { | |
| 850 | - wp_enqueue_style('wpfnl-admin-app', plugin_dir_url(__FILE__) . 'assets/css/admin-app.css', [], $this->version, 'all'); | |
| 869 | + /** | |
| 870 | + * Screen hooks the React single-page admin renders on — the WPFunnels | |
| 871 | + * dashboard, plus the alias slug it was first built under. | |
| 872 | + * | |
| 873 | + * The settings page is here too. It normally redirects to `#/settings` | |
| 874 | + * before this ever runs, so nothing loads on the way through; the entry | |
| 875 | + * matters for the case where that redirect is filtered off, when the page | |
| 876 | + * renders the app itself and needs the app's assets to do it. | |
| 877 | + * | |
| 878 | + * @return array Hook suffixes. | |
| 879 | + * @since 3.13.2 | |
| 880 | + */ | |
| 881 | + private function get_react_admin_hooks() | |
| 882 | + { | |
| 883 | + return array( | |
| 884 | + 'toplevel_page_' . WPFNL_MAIN_PAGE_SLUG, | |
| 885 | + 'wpfunnels_page_' . WPFNL_REACT_ADMIN_SLUG, | |
| 886 | + 'wpfunnels_page_' . WPFNL_GLOBAL_SETTINGS_SLUG, | |
| 887 | + ); | |
| 888 | + } | |
| 889 | + | |
| 890 | + | |
| 891 | + /** | |
| 892 | + * Resolve what the automation screen still needs before it can be used. | |
| 893 | + * | |
| 894 | + * Automations run on four things: WPFunnels and WPFunnels Pro with an | |
| 895 | + * activated licence, and Mail Mint and Mail Mint Pro with an activated | |
| 896 | + * licence. Each of the two Pro stacks walks the same ladder — not on the | |
| 897 | + * site, on the site but switched off, running but unlicensed, satisfied — | |
| 898 | + * and each rung needs a different next step, so the whole thing is resolved | |
| 899 | + * here rather than in the React app: only PHP can tell an uninstalled plugin | |
| 900 | + * from a deactivated one, and only PHP knows the licence state. | |
| 901 | + * | |
| 902 | + * @return array List of dependency descriptors, in the order they should be | |
| 903 | + * presented. | |
| 904 | + * @since 3.13.3 | |
| 905 | + */ | |
| 906 | + private function get_automation_dependencies() | |
| 907 | + { | |
| 908 | + return array( | |
| 909 | + $this->get_wpfnl_pro_dependency(), | |
| 910 | + $this->get_mail_mint_dependency(), | |
| 911 | + ); | |
| 912 | + } | |
| 913 | + | |
| 914 | + | |
| 915 | + /** | |
| 916 | + * Dependency descriptor for the WPFunnels Pro half of the ladder. | |
| 917 | + * | |
| 918 | + * WPFunnels Pro is not on wordpress.org, so the "not installed" rung is a | |
| 919 | + * purchase link rather than a one-click install. | |
| 920 | + * | |
| 921 | + * @return array Dependency descriptor. | |
| 922 | + * @since 3.13.3 | |
| 923 | + */ | |
| 924 | + private function get_wpfnl_pro_dependency() | |
| 925 | + { | |
| 926 | + $base = array( | |
| 927 | + 'id' => 'wpfunnels-pro', | |
| 928 | + 'name' => __('WPFunnels Pro', 'wpfnl'), | |
| 929 | + 'basename' => self::WPFNL_PRO_BASENAME, | |
| 930 | + 'slug' => 'wpfunnels-pro', | |
| 931 | + 'action' => '', | |
| 932 | + 'url' => '', | |
| 933 | + 'newTab' => false, | |
| 934 | + ); | |
| 935 | + | |
| 936 | + if ((bool) apply_filters('wpfunnels/is_pro_license_activated', false)) { | |
| 937 | + return array_merge($base, array( | |
| 938 | + 'state' => 'satisfied', | |
| 939 | + 'message' => '', | |
| 940 | + 'label' => '', | |
| 941 | + )); | |
| 851 | 942 | } |
| 852 | 943 | |
| 944 | + if (Wpfnl_functions::is_plugin_activated(self::WPFNL_PRO_BASENAME)) { | |
| 945 | + return array_merge($base, array( | |
| 946 | + 'state' => 'needs_license', | |
| 947 | + 'message' => __('WPFunnels Pro is active, but its license has not been activated yet.', 'wpfnl'), | |
| 948 | + 'label' => __('Activate License', 'wpfnl'), | |
| 949 | + 'url' => esc_url_raw(add_query_arg(array('page' => 'wpf-license'), admin_url('admin.php'))), | |
| 950 | + )); | |
| 951 | + } | |
| 853 | 952 | |
| 854 | - if (in_array($hook, $this->page_hooks)) { | |
| 855 | - wp_enqueue_style('wp-color-picker'); | |
| 856 | - wp_enqueue_style($this->plugin_name . '-jquery-ui', plugin_dir_url(__FILE__) . 'assets/css/jquery-ui.min.css', [], $this->version, 'all'); | |
| 953 | + if (Wpfnl_functions::is_plugin_installed(self::WPFNL_PRO_BASENAME)) { | |
| 954 | + return array_merge($base, array( | |
| 955 | + 'state' => 'needs_activation', | |
| 956 | + 'message' => __('WPFunnels Pro is installed but currently inactive.', 'wpfnl'), | |
| 957 | + 'label' => __('Activate WPFunnels Pro', 'wpfnl'), | |
| 958 | + 'action' => 'activate', | |
| 959 | + )); | |
| 960 | + } | |
| 857 | 961 | |
| 858 | - wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'assets/css/wpfnl-admin.css', [], $this->version, 'all'); | |
| 962 | + return array_merge($base, array( | |
| 963 | + 'state' => 'not_installed', | |
| 964 | + 'message' => __('Automations need WPFunnels Pro, which is not installed on this site.', 'wpfnl'), | |
| 965 | + 'label' => __('Upgrade to Pro', 'wpfnl'), | |
| 966 | + 'url' => 'https://getwpfunnels.com/pricing/', | |
| 967 | + 'newTab' => true, | |
| 968 | + )); | |
| 969 | + } | |
| 859 | 970 | |
| 860 | - do_action('wpfunnels_after_styles_loaded'); | |
| 971 | + | |
| 972 | + /** | |
| 973 | + * Dependency descriptor for the Mail Mint half of the ladder. | |
| 974 | + * | |
| 975 | + * The free plugin comes from wordpress.org, so its first two rungs are | |
| 976 | + * one-click: wpfunnels/v1/activate-plugin installs the package when it is | |
| 977 | + * missing and activates it either way. Mail Mint Pro is a paid plugin, so | |
| 978 | + * its "not installed" rung is a purchase link. | |
| 979 | + * | |
| 980 | + * @return array Dependency descriptor. | |
| 981 | + * @since 3.13.3 | |
| 982 | + */ | |
| 983 | + private function get_mail_mint_dependency() | |
| 984 | + { | |
| 985 | + $base = array( | |
| 986 | + 'id' => 'mail-mint', | |
| 987 | + 'name' => __('Mail Mint', 'wpfnl'), | |
| 988 | + 'basename' => self::MAIL_MINT_BASENAME, | |
| 989 | + 'slug' => 'mail-mint', | |
| 990 | + 'action' => '', | |
| 991 | + 'url' => '', | |
| 992 | + 'newTab' => false, | |
| 993 | + ); | |
| 994 | + | |
| 995 | + if (Wpfnl_functions::is_mail_mint_pro_license_active() && Wpfnl_functions::is_mail_mint_free_active()) { | |
| 996 | + return array_merge($base, array( | |
| 997 | + 'state' => 'satisfied', | |
| 998 | + 'message' => '', | |
| 999 | + 'label' => '', | |
| 1000 | + )); | |
| 861 | 1001 | } |
| 1002 | + | |
| 1003 | + if (!Wpfnl_functions::is_plugin_installed(self::MAIL_MINT_BASENAME)) { | |
| 1004 | + return array_merge($base, array( | |
| 1005 | + 'state' => 'not_installed', | |
| 1006 | + 'message' => __('Automations need Mail Mint, which is not installed on this site.', 'wpfnl'), | |
| 1007 | + 'label' => __('Install Mail Mint', 'wpfnl'), | |
| 1008 | + 'action' => 'install', | |
| 1009 | + )); | |
| 1010 | + } | |
| 1011 | + | |
| 1012 | + if (!Wpfnl_functions::is_mail_mint_free_active()) { | |
| 1013 | + return array_merge($base, array( | |
| 1014 | + 'state' => 'needs_activation', | |
| 1015 | + 'message' => __('Mail Mint is installed but currently inactive.', 'wpfnl'), | |
| 1016 | + 'label' => __('Activate Mail Mint', 'wpfnl'), | |
| 1017 | + 'action' => 'activate', | |
| 1018 | + )); | |
| 1019 | + } | |
| 1020 | + | |
| 1021 | + // Mail Mint is running; the rest of the ladder is about its Pro plugin. | |
| 1022 | + if (Wpfnl_functions::is_plugin_activated(self::MAIL_MINT_PRO_BASENAME)) { | |
| 1023 | + return array_merge($base, array( | |
| 1024 | + 'state' => 'needs_license', | |
| 1025 | + 'name' => __('Mail Mint Pro', 'wpfnl'), | |
| 1026 | + 'basename' => self::MAIL_MINT_PRO_BASENAME, | |
| 1027 | + 'message' => __('Mail Mint Pro is active, but its license has not been activated yet.', 'wpfnl'), | |
| 1028 | + 'label' => __('Activate License', 'wpfnl'), | |
| 1029 | + 'url' => esc_url_raw(admin_url('admin.php?page=mrm-admin#/settings/license')), | |
| 1030 | + )); | |
| 1031 | + } | |
| 1032 | + | |
| 1033 | + if (Wpfnl_functions::is_plugin_installed(self::MAIL_MINT_PRO_BASENAME)) { | |
| 1034 | + return array_merge($base, array( | |
| 1035 | + 'state' => 'needs_activation', | |
| 1036 | + 'name' => __('Mail Mint Pro', 'wpfnl'), | |
| 1037 | + 'basename' => self::MAIL_MINT_PRO_BASENAME, | |
| 1038 | + 'slug' => 'mail-mint-pro', | |
| 1039 | + 'message' => __('Mail Mint Pro is installed but currently inactive.', 'wpfnl'), | |
| 1040 | + 'label' => __('Activate Mail Mint Pro', 'wpfnl'), | |
| 1041 | + 'action' => 'activate', | |
| 1042 | + )); | |
| 1043 | + } | |
| 1044 | + | |
| 1045 | + return array_merge($base, array( | |
| 1046 | + 'state' => 'not_installed', | |
| 1047 | + 'name' => __('Mail Mint Pro', 'wpfnl'), | |
| 1048 | + 'basename' => self::MAIL_MINT_PRO_BASENAME, | |
| 1049 | + 'slug' => 'mail-mint-pro', | |
| 1050 | + 'message' => __('Automations need Mail Mint Pro with an activated license.', 'wpfnl'), | |
| 1051 | + 'label' => __('Get Mail Mint Pro', 'wpfnl'), | |
| 1052 | + 'url' => 'https://getwpfunnels.com/pricing/#mail-mint', | |
| 1053 | + 'newTab' => true, | |
| 1054 | + )); | |
| 862 | 1055 | } |
| 863 | 1056 | |
| 1057 | + | |
| 864 | 1058 | /** |
| 1059 | + * Work out what the header's Pro call to action should say and where it | |
| 1060 | + * should go. | |
| 1061 | + * | |
| 1062 | + * There are three ways an install can fall short of a licensed Pro, and each | |
| 1063 | + * needs a different next step — telling someone to buy Pro when the plugin is | |
| 1064 | + * sitting deactivated in their plugins list is a dead end. Resolved here | |
| 1065 | + * rather than in the React app because the activation link needs a nonce and | |
| 1066 | + * a capability check. | |
| 1067 | + * | |
| 1068 | + * @return array|null CTA descriptor, or null when Pro is fully licensed and | |
| 1069 | + * no call to action belongs in the header. | |
| 1070 | + * @since 3.13.2 | |
| 1071 | + */ | |
| 1072 | + private function get_pro_cta() | |
| 1073 | + { | |
| 1074 | + if (!function_exists('get_plugins')) { | |
| 1075 | + require_once ABSPATH . 'wp-admin/includes/plugin.php'; | |
| 1076 | + } | |
| 1077 | + | |
| 1078 | + $basename = 'wpfunnels-pro/wpfnl-pro.php'; | |
| 1079 | + | |
| 1080 | + if ((bool) apply_filters('wpfunnels/is_pro_license_activated', false)) { | |
| 1081 | + return null; | |
| 1082 | + } | |
| 1083 | + | |
| 1084 | + // Installed and running, but unlicensed — the features stay locked until | |
| 1085 | + // the licence is activated, so send them to the licence screen. | |
| 1086 | + if (Wpfnl_functions::is_plugin_activated($basename)) { | |
| 1087 | + return array( | |
| 1088 | + 'state' => 'needs_license', | |
| 1089 | + 'label' => __('Activate License', 'wpfnl'), | |
| 1090 | + 'title' => __('Activate your WPFunnels Pro license to unlock Pro features', 'wpfnl'), | |
| 1091 | + 'url' => esc_url_raw(add_query_arg(array('page' => 'wpf-license'), admin_url('admin.php'))), | |
| 1092 | + 'newTab' => false, | |
| 1093 | + ); | |
| 1094 | + } | |
| 1095 | + | |
| 1096 | + // Present but deactivated — one click away, no purchase needed. | |
| 1097 | + if (Wpfnl_functions::is_plugin_installed($basename)) { | |
| 1098 | + $plugins_url = admin_url('plugins.php'); | |
| 1099 | + $can_activate = current_user_can('activate_plugins'); | |
| 1100 | + | |
| 1101 | + /* | |
| 1102 | + * Built with add_query_arg rather than wp_nonce_url on purpose. | |
| 1103 | + * wp_nonce_url() returns esc_html()'d output, so its separators come | |
| 1104 | + * back as `&`. That is correct for a URL echoed straight into | |
| 1105 | + * markup — the HTML parser decodes it — but this one travels through | |
| 1106 | + * wp_localize_script and is applied by React with setAttribute, | |
| 1107 | + * where nothing decodes entities. The literal `&` would split | |
| 1108 | + * the query string into junk parameters, `plugin` and `_wpnonce` | |
| 1109 | + * would arrive empty, and WordPress would answer the click with | |
| 1110 | + * "The link you followed has expired." | |
| 1111 | + * | |
| 1112 | + * wp_localize_script's own html_entity_decode() does not save us | |
| 1113 | + * either: it only walks top-level scalars, and this URL is nested | |
| 1114 | + * inside the CTA array. | |
| 1115 | + * | |
| 1116 | + * Without the capability the one-click link would only bounce off a | |
| 1117 | + * permission check, so point at the plugins list instead. | |
| 1118 | + */ | |
| 1119 | + $url = $can_activate | |
| 1120 | + ? add_query_arg( | |
| 1121 | + array( | |
| 1122 | + 'action' => 'activate', | |
| 1123 | + 'plugin' => rawurlencode($basename), | |
| 1124 | + '_wpnonce' => wp_create_nonce('activate-plugin_' . $basename), | |
| 1125 | + ), | |
| 1126 | + $plugins_url | |
| 1127 | + ) | |
| 1128 | + : $plugins_url; | |
| 1129 | + | |
| 1130 | + return array( | |
| 1131 | + 'state' => 'needs_activation', | |
| 1132 | + 'label' => __('Activate Pro', 'wpfnl'), | |
| 1133 | + 'title' => __('WPFunnels Pro is installed but not active — activate it to unlock Pro features', 'wpfnl'), | |
| 1134 | + 'url' => esc_url_raw($url), | |
| 1135 | + 'newTab' => false, | |
| 1136 | + ); | |
| 1137 | + } | |
| 1138 | + | |
| 1139 | + // Not on the site at all. | |
| 1140 | + return array( | |
| 1141 | + 'state' => 'not_installed', | |
| 1142 | + 'label' => __('Upgrade to Pro', 'wpfnl'), | |
| 1143 | + 'title' => __('Upgrade to WPFunnels Pro', 'wpfnl'), | |
| 1144 | + 'url' => 'https://getwpfunnels.com/pricing/', | |
| 1145 | + 'newTab' => true, | |
| 1146 | + ); | |
| 1147 | + } | |
| 1148 | + | |
| 1149 | + | |
| 1150 | + /** | |
| 1151 | + * Enqueue the React single-page admin bundle. | |
| 1152 | + * | |
| 1153 | + * Deliberately kept out of the `$page_hooks` group: the React admin ships | |
| 1154 | + * its own namespaced stylesheet (every class prefixed `rwpfnl`) and must not | |
| 1155 | + * inherit the legacy admin CSS, which is written against the Vue markup. | |
| 1156 | + * | |
| 1157 | + * @param string $hook Current admin screen hook suffix. | |
| 1158 | + * | |
| 1159 | + * @return void | |
| 1160 | + * @since 3.13.2 | |
| 1161 | + */ | |
| 1162 | + /** | |
| 1163 | + * Data the React template library screen needs. | |
| 1164 | + * | |
| 1165 | + * The same facts the Vue library read off `WPFunnelVars` and | |
| 1166 | + * `template_library_object`, narrowed to what the standalone Templates page | |
| 1167 | + * actually uses. The listing itself still comes from the REST API; this is | |
| 1168 | + * only the context the listing is read in. | |
| 1169 | + * | |
| 1170 | + * @return array Template library context. | |
| 1171 | + * @since 3.13.5 | |
| 1172 | + */ | |
| 1173 | + private function get_template_library_data() | |
| 1174 | + { | |
| 1175 | + $is_pro_license_active = (bool) apply_filters('wpfunnels/is_pro_license_activated', false); | |
| 1176 | + | |
| 1177 | + return array( | |
| 1178 | + 'builder' => Wpfnl_functions::get_builder_type(), | |
| 1179 | + 'isAnyPluginMissing' => Wpfnl_functions::is_any_plugin_missing(), | |
| 1180 | + 'dependencyPlugins' => Wpfnl_functions::get_dependency_plugins_status(), | |
| 1181 | + 'types' => Wpfnl_functions::get_template_types(), | |
| 1182 | + // Store Checkout funnels have their own allowance, so they are not | |
| 1183 | + // counted against this one. | |
| 1184 | + 'activeFunnels' => Wpfnl_functions::count_non_store_checkout_funnels(), | |
| 1185 | + // -1 once the licence lifts the cap. | |
| 1186 | + 'allowedFunnels' => $is_pro_license_active ? -1 : 3, | |
| 1187 | + // The remote-funnel add-on serves the library from elsewhere, and | |
| 1188 | + // this screen then has nothing of its own to fetch. | |
| 1189 | + 'isRemote' => Wpfnl_functions::maybe_remote_funnel(), | |
| 1190 | + ); | |
| 1191 | + } | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + private function enqueue_react_admin_assets($hook) | |
| 1195 | + { | |
| 1196 | + if (!in_array($hook, $this->get_react_admin_hooks(), true)) { | |
| 1197 | + return; | |
| 1198 | + } | |
| 1199 | + | |
| 1200 | + /* | |
| 1201 | + * A production build splits the webpack runtime into its own chunk that | |
| 1202 | + * every entry depends on, so it has to load first. A development build | |
| 1203 | + * (npm run start) keeps the runtime inside each entry and emits no such | |
| 1204 | + * file, hence the existence check rather than a hard dependency. | |
| 1205 | + */ | |
| 1206 | + $runtime_deps = array(); | |
| 1207 | + $runtime_path = WPFNL_DIR . '/admin/assets/dist/runtime/index.min.js'; | |
| 1208 | + | |
| 1209 | + if (file_exists($runtime_path)) { | |
| 1210 | + wp_enqueue_script( | |
| 1211 | + 'wpfnl-webpack-runtime', | |
| 1212 | + plugin_dir_url(__FILE__) . 'assets/dist/runtime/index.min.js', | |
| 1213 | + array(), | |
| 1214 | + $this->version, | |
| 1215 | + true | |
| 1216 | + ); | |
| 1217 | + | |
| 1218 | + $runtime_deps[] = 'wpfnl-webpack-runtime'; | |
| 1219 | + } | |
| 1220 | + | |
| 1221 | + wp_enqueue_script( | |
| 1222 | + 'wpfnl-react-admin', | |
| 1223 | + plugin_dir_url(__FILE__) . 'assets/dist/js/wpfnl-react-admin.min.js', | |
| 1224 | + $runtime_deps, | |
| 1225 | + $this->version, | |
| 1226 | + true | |
| 1227 | + ); | |
| 1228 | + | |
| 1229 | + Wpfnl_functions::enqueue_bundle_style('wpfnl-react-admin', 'wpfnl-react-admin-bundle'); | |
| 1230 | + | |
| 1231 | + // The React admin renders the whole header action group itself — the | |
| 1232 | + // AI Assistant drawer included (see src/admin/components/AiDrawer/) — | |
| 1233 | + // so the Vue global-ai-drawer bundle is NOT loaded on this screen: it | |
| 1234 | + // would find neither of its mount points (#wpfnl-header-actions, | |
| 1235 | + // #wpfnl-global-ai-drawer), and loading it anyway only shipped a second | |
| 1236 | + // copy of the group's markup and styles. | |
| 1237 | + $_is_pro_license_active = (bool) apply_filters('wpfunnels/is_pro_license_activated', false); | |
| 1238 | + | |
| 1239 | + /* | |
| 1240 | + * Everything after the page name in the title WordPress just printed — | |
| 1241 | + * separator, site name, " — WordPress". The React admin is one WordPress | |
| 1242 | + * page with its routes in the hash, so WordPress titles the tab once, | |
| 1243 | + * with this page's own name ("Dashboard"), and every route below it | |
| 1244 | + * inherits that. The app retitles itself per route (see | |
| 1245 | + * src/admin/utils/documentTitle.js) and needs this tail, which is | |
| 1246 | + * translatable and so cannot be rebuilt in JavaScript. | |
| 1247 | + * | |
| 1248 | + * `$admin_title` is built from `$title`, so it always starts with it; | |
| 1249 | + * the prefix check is only there in case an `admin_title` filter has | |
| 1250 | + * rewritten the whole thing, in which case the app keeps what WordPress | |
| 1251 | + * printed for the page name it resolves. Entities such as `‹` | |
| 1252 | + * survive here: wp_localize_script() decodes them on the way out. | |
| 1253 | + */ | |
| 1254 | + $_document_title_suffix = ''; | |
| 1255 | + | |
| 1256 | + if (isset($GLOBALS['title'], $GLOBALS['admin_title'])) { | |
| 1257 | + $_page_title = (string) $GLOBALS['title']; | |
| 1258 | + $_admin_title = (string) $GLOBALS['admin_title']; | |
| 1259 | + | |
| 1260 | + if ('' !== $_page_title && 0 === strpos($_admin_title, $_page_title)) { | |
| 1261 | + $_document_title_suffix = substr($_admin_title, strlen($_page_title)); | |
| 1262 | + } | |
| 1263 | + } | |
| 1264 | + | |
| 1265 | + // Mail Mint's install state is read from get_plugins() below. | |
| 1266 | + if (!function_exists('get_plugins')) { | |
| 1267 | + require_once ABSPATH . 'wp-admin/includes/plugin.php'; | |
| 1268 | + } | |
| 1269 | + | |
| 1270 | + /* | |
| 1271 | + * Core's plugin updater. The template library route offers to install | |
| 1272 | + * the page builder its templates are authored for when that plugin is | |
| 1273 | + * missing, and `wp.updates` is what does the installing. | |
| 1274 | + */ | |
| 1275 | + wp_enqueue_script('updates'); | |
| 1276 | + | |
| 1277 | + wp_localize_script( | |
| 1278 | + 'wpfnl-react-admin', | |
| 1279 | + 'RWPFNLAdmin', | |
| 1280 | + array( | |
| 1281 | + 'rest_api_url' => get_rest_url(), | |
| 1282 | + 'nonce' => wp_create_nonce('wp_rest'), | |
| 1283 | + 'security' => wp_create_nonce('wpfnl-admin'), | |
| 1284 | + 'admin_url' => admin_url(), | |
| 1285 | + 'image_path' => WPFNL_URL . 'admin/assets/images', | |
| 1286 | + // Base URL the bundle resolves its async chunks against — the | |
| 1287 | + // canvas editor is code-split (see src/admin/public-path.js). | |
| 1288 | + 'dist_url' => WPFNL_URL . 'admin/assets/dist/', | |
| 1289 | + 'priceConfig' => Wpfnl_functions::get_wc_price_config(), | |
| 1290 | + 'isProActivated' => apply_filters('wpfunnels/is_wpfnl_pro_active', false), | |
| 1291 | + 'isWPFIntegrationActive' => apply_filters('wpfunnels/is_integration_license_active', false), | |
| 1292 | + /* | |
| 1293 | + * Mail Mint's licence state, which decides whether the automation | |
| 1294 | + * route shows the promo screen at all or hands the visitor to the | |
| 1295 | + * legacy automation list. | |
| 1296 | + */ | |
| 1297 | + 'isMailMintProLicenseActive' => Wpfnl_functions::is_mail_mint_pro_license_active(), | |
| 1298 | + /* | |
| 1299 | + * What the automation screen still needs, rung by rung, with the | |
| 1300 | + * next step for each. Plugin-installing rungs carry a basename and | |
| 1301 | + * slug the screen posts to wpfunnels/v1/activate-plugin; the rest | |
| 1302 | + * carry a URL. | |
| 1303 | + */ | |
| 1304 | + 'automationDependencies' => $this->get_automation_dependencies(), | |
| 1305 | + 'canInstallPlugins' => current_user_can('install_plugins'), | |
| 1306 | + 'canActivatePlugins' => current_user_can('activate_plugins'), | |
| 1307 | + 'getText' => Wpfnl_functions::get_text(), | |
| 1308 | + 'startOfWeek' => (int) get_option('start_of_week', 0), | |
| 1309 | + /* | |
| 1310 | + * Whether the onboarding checklist was on show last time the | |
| 1311 | + * dashboard asked — 'yes', 'no', or absent when unknown. Only | |
| 1312 | + * decides whether to hold space for it while its real status | |
| 1313 | + * loads; the status itself still comes from the REST call. | |
| 1314 | + */ | |
| 1315 | + 'onboardingHint' => get_transient(\WPFunnels\Rest\Controllers\DashboardController::ONBOARDING_VISIBILITY_KEY), | |
| 1316 | + /* | |
| 1317 | + * Header action group data. It has to ride on this handle, not on | |
| 1318 | + * the AI-drawer handle the Vue group used: WordPress prints a | |
| 1319 | + * script's localized data immediately before that script's own | |
| 1320 | + * tag, and the React bundle is enqueued first — so anything | |
| 1321 | + * attached to the drawer does not exist yet when React renders. | |
| 1322 | + */ | |
| 1323 | + 'isProLicenseActive' => $_is_pro_license_active, | |
| 1324 | + 'upgradeUrl' => 'https://getwpfunnels.com/pricing/', | |
| 1325 | + 'proCta' => $this->get_pro_cta(), | |
| 1326 | + 'documentTitleSuffix' => $_document_title_suffix, | |
| 1327 | + /* | |
| 1328 | + * What the template library route runs on. Every value here is | |
| 1329 | + * something only PHP can answer — which page builder the site is | |
| 1330 | + * set up for and whether its plugin is actually there, how much | |
| 1331 | + * of an unlicensed install's funnel allowance is left, which | |
| 1332 | + * template types this install has. Arrives as a real object: | |
| 1333 | + * wp_localize_script only stringifies top-level scalars. | |
| 1334 | + */ | |
| 1335 | + 'templateLibrary' => $this->get_template_library_data(), | |
| 1336 | + /* | |
| 1337 | + * The two settings panels that are still Vue apps of their own, | |
| 1338 | + * fetched by the settings screen when one of them is opened. | |
| 1339 | + */ | |
| 1340 | + 'settingsPanelApps' => $this->get_settings_panel_apps(), | |
| 1341 | + ) | |
| 1342 | + ); | |
| 1343 | + | |
| 1344 | + $this->enqueue_settings_panel_apps(); | |
| 1345 | + } | |
| 1346 | + | |
| 1347 | + | |
| 1348 | + /** | |
| 1349 | + * The two settings panels that are still Vue apps of their own. | |
| 1350 | + * | |
| 1351 | + * "AI Assistant" and "MCP for AI Agents" each own their fields and save them | |
| 1352 | + * through their own REST routes, so the React settings screen renders a | |
| 1353 | + * mount point and asks those bundles to mount into it rather than | |
| 1354 | + * reimplementing them (see src/admin/pages/Settings/VuePanel.jsx). The | |
| 1355 | + * bundles are unchanged — they still mount themselves on the legacy settings | |
| 1356 | + * page, where the mount point is printed by PHP. | |
| 1357 | + * | |
| 1358 | + * @return void | |
| 1359 | + * @since 3.13.8 | |
| 1360 | + */ | |
| 1361 | + private function enqueue_settings_panel_apps() | |
| 1362 | + { | |
| 1363 | + $_current_user = wp_get_current_user(); | |
| 1364 | + $_common = array( | |
| 1365 | + 'username' => $_current_user ? $_current_user->user_login : '', | |
| 1366 | + 'rest_url' => esc_url_raw(get_rest_url()), | |
| 1367 | + 'nonce' => wp_create_nonce('wp_rest'), | |
| 1368 | + 'admin_url' => esc_url_raw(admin_url()), | |
| 1369 | + ); | |
| 1370 | + | |
| 1371 | + /* | |
| 1372 | + * The globals the two bundles read, printed before the React bundle so | |
| 1373 | + * they are there whenever a bundle is pulled in later. They are a few | |
| 1374 | + * hundred bytes; the bundles themselves are megabytes, which is why | |
| 1375 | + * only the data is printed up front. | |
| 1376 | + */ | |
| 1377 | + wp_add_inline_script( | |
| 1378 | + 'wpfnl-react-admin', | |
| 1379 | + 'window.WPFnlMcpSettings = ' . wp_json_encode(array_merge($_common, array('site_url' => esc_url_raw(home_url())))) . ';' . | |
| 1380 | + 'window.WPFnlAiAssistantSettings = ' . wp_json_encode($_common) . ';', | |
| 1381 | + 'before' | |
| 1382 | + ); | |
| 1383 | + } | |
| 1384 | + | |
| 1385 | + | |
| 1386 | + /** | |
| 1387 | + * Where the settings screen fetches its two Vue panels from. | |
| 1388 | + * | |
| 1389 | + * Neither bundle is enqueued: only one settings panel in the app needs | |
| 1390 | + * either of them, and the route is in the fragment, which the server never | |
| 1391 | + * sees — so loading them on every screen of the admin would cost every | |
| 1392 | + * screen megabytes for a panel most visitors never open. The panel loads | |
| 1393 | + * what it needs when it is opened (see pages/Settings/panels/VuePanel.jsx). | |
| 1394 | + * | |
| 1395 | + * The stylesheet is empty on a development build, which injects its styles | |
| 1396 | + * from JavaScript and emits none. | |
| 1397 | + * | |
| 1398 | + * @return array Script and stylesheet URL per panel. | |
| 1399 | + * @since 3.13.8 | |
| 1400 | + */ | |
| 1401 | + private function get_settings_panel_apps() | |
| 1402 | + { | |
| 1403 | + return array( | |
| 1404 | + 'aiAssistant' => array( | |
| 1405 | + 'script' => plugin_dir_url(__FILE__) . 'assets/dist/js/ai-assistant-settings.min.js', | |
| 1406 | + 'style' => Wpfnl_functions::get_bundle_style_url('ai-assistant-settings'), | |
| 1407 | + ), | |
| 1408 | + 'mcp' => array( | |
| 1409 | + 'script' => plugin_dir_url(__FILE__) . 'assets/dist/js/mcp-agent-settings.min.js', | |
| 1410 | + 'style' => Wpfnl_functions::get_bundle_style_url('mcp-agent-settings'), | |
| 1411 | + ), | |
| 1412 | + ); | |
| 1413 | + } | |
| 1414 | + | |
| 1415 | + | |
| 1416 | + /** | |
| 865 | 1417 | * Register the JavaScript for the admin area. |
| 866 | 1418 | * |
| 867 | 1419 | * @since 1.0.0 |
| 868 | 1420 | */ |
| @@ -883,8 +1435,11 @@ | ||
| 883 | 1435 | |
| 884 | 1436 | $screen = get_current_screen(); |
| 885 | 1437 | |
| 886 | 1438 | |
| 1439 | + $this->enqueue_react_admin_assets($hook); | |
| 1440 | + | |
| 1441 | + | |
| 887 | 1442 | if ('dashboard' === $screen->id || 'plugins' === $screen->id) { |
| 888 | 1443 | wp_enqueue_script($this->plugin_name . '-global', plugin_dir_url(__FILE__) . 'assets/js/wpfnl-global.js', ['jquery', 'jquery-ui-sortable'], $this->version, true); |
| 889 | 1444 | } |
| 890 | 1445 | |
| @@ -916,83 +1471,56 @@ | ||
| 916 | 1471 | ) |
| 917 | 1472 | ); |
| 918 | 1473 | } |
| 919 | 1474 | |
| 920 | - if ('wpfunnels_page_' . WPFNL_AI_FUNNEL_PAGE_SLUG === $hook) { | |
| 921 | - wp_enqueue_script('wpfnl-ai-funnel-app', plugin_dir_url(__FILE__) . 'assets/dist/js/ai-funnel-app.min.js', [], $this->version, true); | |
| 922 | - Wpfnl_functions::enqueue_bundle_style( 'ai-funnel-app', 'wpfnl-ai-funnel-app-bundle' ); | |
| 923 | - $_ai_pro_active = Wpfnl_functions::is_wpfnl_pro_activated(); | |
| 924 | - $_ai_enabled = \WPFunnels\AI\Settings\AISettings::isEnabled(); | |
| 925 | - wp_localize_script('wpfnl-ai-funnel-app', 'WPFNAIFunnel', array( | |
| 926 | - 'adminUrl' => esc_url_raw(admin_url('admin.php')), | |
| 927 | - 'pageUrl' => esc_url_raw(add_query_arg(array( | |
| 928 | - 'page' => WPFNL_AI_FUNNEL_PAGE_SLUG, | |
| 929 | - ), admin_url('admin.php'))), | |
| 930 | - 'funnelsUrl' => esc_url_raw(add_query_arg(array( | |
| 931 | - 'page' => WPFNL_FUNNEL_PAGE_SLUG, | |
| 932 | - ), admin_url('admin.php'))), | |
| 933 | - // The combined "AI Settings" tab was split into "MCP for AI Agents" | |
| 934 | - // and "AI Assistant" — deep-link to the AI Assistant tab, its direct | |
| 935 | - // feature successor. | |
| 936 | - 'settingsUrl' => esc_url_raw( add_query_arg( array( 'page' => WPFNL_GLOBAL_SETTINGS_SLUG ), admin_url( 'admin.php' ) ) . '#ai-assistant-settings' ), | |
| 937 | - 'restApiUrl' => esc_url_raw( get_rest_url() ), | |
| 938 | - 'restNonce' => wp_create_nonce( 'wp_rest' ), | |
| 939 | - // The copilot client reads rest_url/nonce; without these it falls | |
| 940 | - // back to an empty nonce and every REST call 401s. | |
| 941 | - 'rest_url' => esc_url_raw( get_rest_url() ), | |
| 942 | - 'nonce' => wp_create_nonce( 'wp_rest' ), | |
| 943 | - 'rest_nonce' => wp_create_nonce( 'wp_rest' ), | |
| 944 | - 'settingsPage' => esc_url_raw( add_query_arg( array( 'page' => WPFNL_GLOBAL_SETTINGS_SLUG ), admin_url( 'admin.php' ) ) . '#ai-assistant-settings' ), | |
| 945 | - 'isProActive' => $_ai_pro_active ? 'yes' : 'no', | |
| 946 | - 'isAiEnabled' => $_ai_enabled ? 'yes' : 'no', | |
| 1475 | + // Store Checkout without WooCommerce: power the "Plugin Required" modal | |
| 1476 | + // rendered by admin/modules/store-checkout/views/woocommerce-required.php. | |
| 1477 | + if ('wpfunnels_page_' . WPFNL_STORE_CHECKOUT_SLUG === $hook && !Wpfnl_functions::is_wc_active()) { | |
| 1478 | + $wc_plugin = 'woocommerce/woocommerce.php'; | |
| 1479 | + $wc_status = Wpfnl_functions::is_plugin_installed($wc_plugin) ? 'inactive' : 'not-installed'; | |
| 1480 | + $wc_label = 'not-installed' === $wc_status | |
| 1481 | + ? __('Install WooCommerce', 'wpfnl') | |
| 1482 | + : __('Activate WooCommerce', 'wpfnl'); | |
| 1483 | + | |
| 1484 | + wp_enqueue_script( | |
| 1485 | + $this->plugin_name . '-store-checkout-woo', | |
| 1486 | + plugin_dir_url(__FILE__) . 'assets/js/wpfnl-store-checkout-woo.js', | |
| 1487 | + ['jquery', 'updates'], | |
| 1488 | + $this->version, | |
| 1489 | + true | |
| 1490 | + ); | |
| 1491 | + wp_localize_script($this->plugin_name . '-store-checkout-woo', 'wpfnlStoreCheckoutWoo', array( | |
| 1492 | + 'ajaxUrl' => admin_url('admin-ajax.php'), | |
| 1493 | + 'nonce' => wp_create_nonce('wpfnl_activate_plugin_nonce'), | |
| 1494 | + 'plugin' => $wc_plugin, | |
| 1495 | + 'slug' => 'woocommerce', | |
| 1496 | + 'status' => $wc_status, | |
| 1497 | + 'buttonLabel' => $wc_label, | |
| 1498 | + 'i18n' => array( | |
| 1499 | + 'installing' => __('Installing', 'wpfnl'), | |
| 1500 | + 'activating' => __('Activating', 'wpfnl'), | |
| 1501 | + 'redirecting' => __('Redirecting', 'wpfnl'), | |
| 1502 | + 'error' => __('Something went wrong. Please install and activate WooCommerce manually.', 'wpfnl'), | |
| 1503 | + ), | |
| 947 | 1504 | )); |
| 948 | 1505 | } |
| 949 | 1506 | |
| 950 | - if ( 'wpfunnels_page_' . WPFNL_GLOBAL_SETTINGS_SLUG === $hook ) { | |
| 951 | - $_current_user = wp_get_current_user(); | |
| 952 | - $_settings_common_data = array( | |
| 953 | - 'username' => $_current_user ? $_current_user->user_login : '', | |
| 954 | - 'rest_url' => esc_url_raw( get_rest_url() ), | |
| 955 | - 'nonce' => wp_create_nonce( 'wp_rest' ), | |
| 956 | - 'admin_url' => esc_url_raw( admin_url() ), | |
| 957 | - ); | |
| 958 | 1507 | |
| 959 | - wp_enqueue_script( 'wpfnl-mcp-agent-settings', plugin_dir_url( __FILE__ ) . 'assets/dist/js/mcp-agent-settings.min.js', [], $this->version, true ); | |
| 960 | - Wpfnl_functions::enqueue_bundle_style( 'mcp-agent-settings', 'wpfnl-mcp-agent-settings-bundle' ); | |
| 961 | - wp_localize_script( | |
| 962 | - 'wpfnl-mcp-agent-settings', | |
| 963 | - 'WPFnlMcpSettings', | |
| 964 | - array_merge( | |
| 965 | - $_settings_common_data, | |
| 966 | - array( | |
| 967 | - 'site_url' => esc_url_raw( home_url() ), | |
| 968 | - ) | |
| 969 | - ) | |
| 970 | - ); | |
| 971 | - | |
| 972 | - wp_enqueue_script( 'wpfnl-ai-assistant-settings', plugin_dir_url( __FILE__ ) . 'assets/dist/js/ai-assistant-settings.min.js', [], $this->version, true ); | |
| 973 | - Wpfnl_functions::enqueue_bundle_style( 'ai-assistant-settings', 'wpfnl-ai-assistant-settings-bundle' ); | |
| 974 | - wp_localize_script( 'wpfnl-ai-assistant-settings', 'WPFnlAiAssistantSettings', $_settings_common_data ); | |
| 975 | - } | |
| 976 | - | |
| 977 | 1508 | if (in_array($hook, $this->page_hooks)) { |
| 978 | 1509 | |
| 979 | 1510 | // Admin header actions — the Knowledge Base, AI Assistant, Settings |
| 980 | 1511 | // and Upgrade group rendered into #wpfnl-header-actions from every |
| 981 | - // WPFunnels admin screen. Reuses the same WPFNAIFunnel global name | |
| 982 | - // the full-page ai-funnel-app bundle localizes (see above) so | |
| 983 | - // admin/src/copilot/services/ai.js works unmodified; safe because | |
| 984 | - // $this->page_hooks and the AI-funnel page hook never match on the | |
| 985 | - // same request. | |
| 986 | - $_settings_page_url = add_query_arg( array( 'page' => WPFNL_GLOBAL_SETTINGS_SLUG ), admin_url( 'admin.php' ) ); | |
| 1512 | + // legacy WPFunnels admin screen. The data goes out under the | |
| 1513 | + // WPFNAIFunnel global because that is the name | |
| 1514 | + // admin/src/copilot/services/ai.js reads, and the drawer shares | |
| 1515 | + // that client with what is left of the copilot bundle. | |
| 1516 | + $_settings_page_url = admin_url( 'admin.php?page=' . WPFNL_MAIN_PAGE_SLUG ) . '#/settings'; | |
| 987 | 1517 | |
| 988 | 1518 | wp_enqueue_script('wpfnl-global-ai-drawer', plugin_dir_url(__FILE__) . 'assets/dist/js/global-ai-drawer.min.js', [], $this->version, true); |
| 989 | 1519 | Wpfnl_functions::enqueue_bundle_style( 'global-ai-drawer', 'wpfnl-global-ai-drawer-bundle' ); |
| 990 | 1520 | wp_localize_script('wpfnl-global-ai-drawer', 'WPFNAIFunnel', array( |
| 991 | 1521 | 'adminUrl' => esc_url_raw( admin_url( 'admin.php' ) ), |
| 992 | - 'settingsUrl' => esc_url_raw( $_settings_page_url . '#ai-assistant-settings' ), | |
| 993 | 1522 | 'settingsPageUrl' => esc_url_raw( $_settings_page_url ), |
| 994 | - 'isSettingsPage' => isset( $_GET['page'] ) && WPFNL_GLOBAL_SETTINGS_SLUG === sanitize_text_field( wp_unslash( $_GET['page'] ) ), | |
| 995 | 1523 | 'isPro' => (bool) apply_filters( 'wpfunnels/is_pro_license_activated', false ), |
| 996 | 1524 | 'upgradeUrl' => 'https://getwpfunnels.com/pricing/', |
| 997 | 1525 | 'restApiUrl' => esc_url_raw( get_rest_url() ), |
| 998 | 1526 | 'restNonce' => wp_create_nonce( 'wp_rest' ), |
| @@ -1152,9 +1680,8 @@ | ||
| 1152 | 1680 | 'isAnyLmsPluginActive' => Wpfnl_functions::is_any_lms_plugin_active(), |
| 1153 | 1681 | 'isLmsSettings' => Wpfnl_functions::is_enable_lms_settings(), |
| 1154 | 1682 | 'isLmsDisbaled' => $this->maybe_lms_settings_disbaled(), |
| 1155 | 1683 | 'lmsVersion' => (defined('WPFNL_PRO_LMS_VERSION') || defined('WPFUNNELS_PRO_LMS_VERSION')) && defined('LEARNDASH_VERSION') ? version_compare(LEARNDASH_VERSION, '4.2.1.1', '>=') : false, |
| 1156 | - 'global_funnel_type' => Wpfnl_functions::get_global_funnel_type(), | |
| 1157 | 1684 | 'individual_funnel_type' => get_post_meta($funnel_id, '_wpfnl_funnel_type', true), |
| 1158 | 1685 | 'isStoreCheckout' => get_post_meta($funnel_id, '_wpfnl_funnel_type', true) === 'store_checkout', |
| 1159 | 1686 | 'isSaleFunnel' => get_post_meta($funnel_id, '_wpfnl_funnel_type', true) === 'wc' || get_post_meta($funnel_id, '_wpfnl_funnel_type', true) === 'store_checkout', |
| 1160 | 1687 | 'gbf_set_condition_steps' => $this->get_gbf_steps($funnel_id), |
| @@ -1197,9 +1724,8 @@ | ||
| 1197 | 1724 | : esc_url_raw(admin_url('admin.php?page=' . WPFNL_FUNNEL_PAGE_SLUG)), |
| 1198 | 1725 | 'settings_url' => esc_url_raw(admin_url('admin.php?page=settings')), |
| 1199 | 1726 | 'home_url' => esc_url_raw(home_url()), |
| 1200 | 1727 | 'funnel_id' => $funnel_id, |
| 1201 | - 'isTemplatePage' => 'wpfunnels_page_wpf_templates' === $hook, | |
| 1202 | 1728 | 'is_pro' => Wpfnl_functions::is_pro_license_activated(), |
| 1203 | 1729 | 'is_ab_tesing_available' => defined('WPFNL_PRO_VERSION') ? version_compare(WPFNL_PRO_VERSION, "1.7.3", ">=") : false, |
| 1204 | 1730 | 'is_webhook_licensed' => Wpfnl_functions::is_webhook_license_activated(), |
| 1205 | 1731 | 'pro_url' => add_query_arg('wpfnl-dashboard', '1', GETWPFUNNEL_PRICING_URL), |
| @@ -1252,26 +1778,9 @@ | ||
| 1252 | 1778 | 'shippingClasses' => Wpfnl_functions::get_shipping_classes(), |
| 1253 | 1779 | ) |
| 1254 | 1780 | ); |
| 1255 | 1781 | |
| 1256 | - // Enqueue the admin dashboard script only on WPFunnels Main Page | |
| 1257 | 1782 | |
| 1258 | - if ($this->is_current_page('wpfunnels')) { | |
| 1259 | - wp_enqueue_script('wpfnl-admin-app', plugin_dir_url(__FILE__) . 'assets/dist/js/wpfnl-admin-app.min.js', array(), $this->version, true); | |
| 1260 | - Wpfnl_functions::enqueue_bundle_style( 'wpfnl-admin-app', 'wpfnl-admin-app-bundle' ); | |
| 1261 | - wp_localize_script('wpfnl-admin-app', 'WPFAdminObj', array( | |
| 1262 | - 'rest_api_url' => get_rest_url(), | |
| 1263 | - 'security' => wp_create_nonce('wpfnl-admin'), | |
| 1264 | - 'nonce' => wp_create_nonce('wp_rest'), | |
| 1265 | - 'priceConfig' => Wpfnl_functions::get_wc_price_config(), | |
| 1266 | - 'isProActivated' => apply_filters('wpfunnels/is_wpfnl_pro_active', false), | |
| 1267 | - 'isWPFIntegrationActive' => apply_filters( 'wpfunnels/is_integration_license_active', false ), | |
| 1268 | - 'getText' => Wpfnl_functions::get_text(), | |
| 1269 | - 'admin_url' => admin_url(), | |
| 1270 | - )); | |
| 1271 | - } | |
| 1272 | - | |
| 1273 | - | |
| 1274 | 1783 | /** |
| 1275 | 1784 | * Localize scripts for funnel window |
| 1276 | 1785 | * |
| 1277 | 1786 | * @var $localize |
| @@ -1713,17 +2222,11 @@ | ||
| 1713 | 2222 | */ |
| 1714 | 2223 | public function wpfnl_deactivated_plugin($filename) |
| 1715 | 2224 | { |
| 1716 | 2225 | if ('woocommerce/woocommerce.php' === $filename) { |
| 1717 | - if (!Wpfnl_functions::is_lms_addon_active()) { | |
| 1718 | - Wpfnl_functions::update_funnel_type_to_lead(); | |
| 1719 | - } | |
| 1720 | 2226 | Wpfnl_functions::wpfnl_delete_transient(); |
| 1721 | 2227 | } |
| 1722 | 2228 | elseif ('sfwd-lms/sfwd_lms.php' === $filename) { |
| 1723 | - if (!Wpfnl_functions::is_wc_active()) { | |
| 1724 | - Wpfnl_functions::update_funnel_type_to_lead(); | |
| 1725 | - } | |
| 1726 | 2229 | Wpfnl_functions::wpfnl_delete_transient(); |
| 1727 | 2230 | } |
| 1728 | 2231 | elseif ('wpfunnels-pro-lms/wpfunnels-pro-lms.php' === $filename) { |
| 1729 | 2232 | Wpfnl_functions::wpfnl_delete_transient(); |
| @@ -1921,9 +2424,15 @@ | ||
| 1921 | 2424 | |
| 1922 | 2425 | if ($screen && (in_array($screen->id, $this->page_hooks) || 'wpfunnels_page_' . WPFNL_AI_FUNNEL_PAGE_SLUG === $screen->id)) { |
| 1923 | 2426 | $classes .= ' wpfunnels-body'; |
| 1924 | 2427 | } |
| 1925 | - | |
| 2428 | + | |
| 2429 | + // The React admin styles the WordPress content column itself, so it gets | |
| 2430 | + // its own class instead of the legacy one. | |
| 2431 | + if ($screen && in_array($screen->id, $this->get_react_admin_hooks(), true)) { | |
| 2432 | + $classes .= ' rwpfnl-page'; | |
| 2433 | + } | |
| 2434 | + | |
| 1926 | 2435 | return $classes; |
| 1927 | 2436 | } |
| 1928 | 2437 | |
| 1929 | 2438 | |