PluginProbe
Lead Generation Form – Contact Form & Form Builder, Survey & Inquiry Forms / 1.2.1
Lead Generation Form – Contact Form & Form Builder, Survey & Inquiry Forms v1.2.1
1.2.0 1.2.1 trunk 1.0.6 1.0.7 1.0.8 1.0.9
lead-generation-form / lead-generation-form.php

lead-generation-form.php in Lead Generation Form – Contact Form & Form Builder, Survey & Inquiry Forms 1.2.1, at lead-generation-form.php

1,454 lines 74.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; /* Exit if accessed directly */
4 }
5
6 /**
7 * Plugin Name: Lead Generation Form - Contact Form & Form Builder
8 * Plugin URI: https://webenvo.com/
9 * Description: Drag and drop contact form builder and lead generation plugin. Build responsive contact forms, inquiry forms, feedback surveys, quote forms, and booking forms.
10 * Version: 1.2.1
11 * Requires at least: 5.6
12 * Requires PHP: 7.4
13 * Author: FARAZFRANK
14 * Author URI: https://profiles.wordpress.org/farazfrank/
15 * License: GPL v2 or later
16 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
17 * Text Domain: lead-generation-form
18 * Domain Path: /languages
19
20 Lead Generation Form is free software: you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation, either version 2 of the License, or
23 any later version.
24
25 Lead Generation Form is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
29
30 You should have received a copy of the GNU General Public License
31 along with Lead Generation Form. If not, see https://webenvo.com/.
32 */
33
34 // Coexistence guard: If Pro is already loaded or active, gracefully yield to Pro.
35 if ( defined( 'WLGF_LOADED' ) ) {
36 return;
37 }
38 $wlgf_active_plugins = (array) get_option( 'active_plugins', array() );
39 if ( in_array( 'lead-generation-form-pro/lead-generation-form.php', $wlgf_active_plugins, true ) || ( is_multisite() && array_key_exists( 'lead-generation-form-pro/lead-generation-form.php', (array) get_site_option( 'active_sitewide_plugins', array() ) ) ) ) {
40 add_action( 'admin_notices', function() {
41 if ( current_user_can( 'activate_plugins' ) ) {
42 echo '<div class="notice notice-warning is-dismissible"><p>' . esc_html__( 'Lead Generation Form (Free) is disabled because Lead Generation Form Pro is active.', 'lead-generation-form' ) . '</p></div>';
43 }
44 } );
45 return;
46 }
47 define( 'WLGF_LOADED', true );
48
49 define( 'WLGF_IS_PRO', false );
50 if ( ! defined( 'WLGF_PLUGIN_VERSION' ) ) {
51 define( 'WLGF_PLUGIN_VERSION', '1.2.1' );
52 }
53
54 /* activation */
55 if ( ! function_exists( 'wlgf_activation' ) ) {
56 function wlgf_activation() {
57 /* update current plugin version */
58 if ( is_admin() ) {
59 $wlgf_active_plugins = (array) get_option( 'active_plugins', array() );
60 if ( in_array( 'lead-generation-form-pro/lead-generation-form.php', $wlgf_active_plugins, true ) ) {
61 if ( ! function_exists( 'deactivate_plugins' ) ) {
62 require_once ABSPATH . 'wp-admin/includes/plugin.php';
63 }
64 deactivate_plugins( plugin_basename( __FILE__ ) );
65 wp_die( esc_html__( 'Lead Generation Form Pro is currently active. Please deactivate Pro before activating the Free version.', 'lead-generation-form' ), 'Plugin Conflict', array( 'back_link' => true ) );
66 }
67
68 // Clean up stale v1.0.9 register_uninstall_hook entry if present
69 $uninstall_plugins = get_option( 'uninstall_plugins' );
70 if ( is_array( $uninstall_plugins ) && isset( $uninstall_plugins[ plugin_basename( __FILE__ ) ] ) ) {
71 unset( $uninstall_plugins[ plugin_basename( __FILE__ ) ] );
72 update_option( 'uninstall_plugins', $uninstall_plugins );
73 }
74
75 if ( ! function_exists( 'get_plugin_data' ) ) {
76 require_once ABSPATH . 'wp-admin/includes/plugin.php';
77 }
78 $wlgf_plugin_data = get_plugin_data( __FILE__ );
79 if ( isset( $wlgf_plugin_data['Version'] ) ) {
80 $wlgf_plugin_version = $wlgf_plugin_data['Version'];
81 update_option( 'wlgf_current_version', $wlgf_plugin_version );
82 }
83
84 //install-script
85 require_once 'includes/install-script.php';
86 }
87 }
88 }
89 register_activation_hook( __FILE__, 'wlgf_activation' );
90
91 /* legacy uninstall safety fallback to prevent fatal errors on upgrades from v1.0.9 */
92 if ( ! function_exists( 'wlgf_uninstall' ) ) {
93 function wlgf_uninstall() {
94 if ( file_exists( plugin_dir_path( __FILE__ ) . 'uninstall.php' ) ) {
95 include_once plugin_dir_path( __FILE__ ) . 'uninstall.php';
96 }
97 }
98 }
99
100 /* de-activation */
101 if ( ! function_exists( 'wlgf_deactivation' ) ) {
102 function wlgf_deactivation(){
103 // update last active plugin version
104 $wlgf_last_version = get_option('wlgf_current_version');
105 if($wlgf_last_version !== ""){
106 update_option('wlgf_last_version', $wlgf_last_version);
107 }
108 }
109 }
110 register_deactivation_hook( __FILE__, 'wlgf_deactivation' );
111
112
113
114 if ( ! function_exists( 'wlgf_check_version' ) ) {
115 function wlgf_check_version() {
116 if ( get_transient( 'wlgf_version_check' ) ) {
117 return;
118 }
119
120 if ( ! function_exists( 'get_plugin_data' ) ) {
121 require_once ABSPATH . 'wp-admin/includes/plugin.php';
122 }
123
124 $wlgf_plugin_data = get_plugin_data( __FILE__ );
125 if ( isset( $wlgf_plugin_data['Version'] ) ) {
126 $wlgf_plugin_version = sanitize_text_field( $wlgf_plugin_data['Version'] );
127 $wlgf_current_version = sanitize_text_field( get_option( 'wlgf_current_version', '1.0' ) );
128
129 if ( version_compare( $wlgf_current_version, $wlgf_plugin_version, '<' ) ) {
130 $wlgf_default_form_options = get_option( 'wlgf_form_1' );
131 if ( is_array( $wlgf_default_form_options ) ) {
132 $wlgf_default_form_options['version'] = $wlgf_plugin_version;
133 update_option( 'wlgf_form_1', $wlgf_default_form_options );
134 }
135 update_option( 'wlgf_current_version', $wlgf_plugin_version );
136 }
137
138 set_transient( 'wlgf_version_check', true, DAY_IN_SECONDS );
139 }
140 }
141 }
142 add_action( 'admin_init', 'wlgf_check_version' );
143
144 // load translation
145 function wlgf_load_translation() {
146 load_plugin_textdomain( 'lead-generation-form', false, dirname( plugin_basename(__FILE__) ) .'/languages' );
147 }
148 add_action( 'plugins_loaded', 'wlgf_load_translation');
149
150 // register styles and scripts for frontend
151 function wlgf_user_register_scripts(){
152 $plugin_version = WLGF_PLUGIN_VERSION;
153 wp_enqueue_script('jquery');
154 wp_register_script( 'wlgf-bootstrap-bundle-min-js', plugin_dir_url(__FILE__) . 'admin/assets/bootstrap-5.3.3/dist/js/bootstrap.bundle.min.js', array('jquery'), '5.3.3' );
155 wp_register_style( 'wlgf-fontawesome-css', plugin_dir_url(__FILE__) . 'admin/assets/fontawesome-free-6.4.2-web/css/all.min.css', array(), '6.4.2', 'all' );
156
157 // shortcode assets
158 wp_register_style( 'wlgf-shortcode-form', plugin_dir_url(__FILE__) . 'includes/assets/css/wlgf-shortcode-form.css', array(), $plugin_version, 'all' );
159
160 wp_register_script('google-recaptcha', 'https://www.google.com/recaptcha/api.js', array(), null, true); // load in footer
161 wp_register_script('wlgf-shortcode-form-js', plugin_dir_url(__FILE__). 'includes/assets/js/wlgf-shortcode-form.js', array('jquery'), $plugin_version, true); // load in footer
162 wp_register_script('wlgf-shortcode-ajax-script', plugin_dir_url(__FILE__) . 'includes/assets/js/wlgf-shortcode-ajax-script.js', array('jquery'), $plugin_version, true);
163 wp_localize_script('wlgf-shortcode-ajax-script', 'ajax_object', array('ajax_url' => admin_url('admin-ajax.php')));
164 }
165 add_action( 'wp_enqueue_scripts', 'wlgf_user_register_scripts' );
166
167 // register styles and scripts for backend
168 function wlgf_admin_register_scripts( $hook = '' ){
169 $plugin_version = WLGF_PLUGIN_VERSION;
170 $is_wlgf_page = isset( $_GET['page'] ) && in_array( sanitize_text_field( wp_unslash( $_GET['page'] ) ), array(
171 'lead-generation-form',
172 'wlgf-manage-forms',
173 'wlgf-form-generator',
174 'wlgf-leads',
175 'wlgf-import-export',
176 'wlgf-settings',
177 ), true );
178
179 // Font Awesome Icons (Admin)
180 wp_register_style( 'wlgf-fontawesome-css', plugin_dir_url(__FILE__) . 'admin/assets/fontawesome-free-6.4.2-web/css/all.min.css', array(), '6.4.2', 'all' );
181
182 // Modern Admin Design System
183 wp_register_style( 'wlgf-admin-ui-css', plugin_dir_url(__FILE__) . 'admin/assets/css/wlgf-admin-ui.css', array( 'wlgf-fontawesome-css' ), $plugin_version, 'all' );
184
185 // DataTables Assets
186 wp_register_style( 'wlgf-datatables-min-css', plugin_dir_url(__FILE__) . 'admin/assets/datatables-2.0.8/datatables.min.css', array(), '2.0.8', 'all' );
187 wp_register_script( 'wlgf-datatables-min-js', plugin_dir_url(__FILE__) . 'admin/assets/datatables-2.0.8/datatables.min.js', array( 'jquery' ), '2.0.8', true );
188
189 // Page-Specific Assets
190 wp_register_style( 'wlgf-manage-forms-css', plugin_dir_url(__FILE__) . 'admin/assets/css/manage-forms.css', array( 'wlgf-admin-ui-css' ), $plugin_version, 'all' );
191 wp_register_script( 'wlgf-manage-forms-js', plugin_dir_url(__FILE__) . 'admin/assets/js/manage-forms.js', array( 'jquery', 'wlgf-datatables-min-js' ), $plugin_version, true );
192
193 wp_register_style( 'wlgf-form-generator-css', plugin_dir_url(__FILE__) . 'admin/assets/css/form-generator.css', array( 'wlgf-admin-ui-css' ), $plugin_version, 'all' );
194 wp_register_script( 'wlgf-form-generator-js', plugin_dir_url(__FILE__) . 'admin/assets/js/form-generator.js', array( 'jquery' ), $plugin_version, true );
195
196 wp_register_style( 'wlgf-leads-css', plugin_dir_url(__FILE__) . 'admin/assets/css/leads.css', array( 'wlgf-admin-ui-css', 'wlgf-datatables-min-css' ), $plugin_version, 'all' );
197 wp_register_script( 'wlgf-leads-js', plugin_dir_url(__FILE__) . 'admin/assets/js/leads.js', array( 'jquery', 'wlgf-datatables-min-js' ), $plugin_version, true );
198
199 wp_register_style( 'wlgf-import-export-css', plugin_dir_url(__FILE__) . 'admin/assets/css/import-export.css', array( 'wlgf-admin-ui-css' ), $plugin_version, 'all' );
200 wp_register_script( 'wlgf-import-export-js', plugin_dir_url(__FILE__) . 'admin/assets/js/import-export.js', array( 'jquery' ), $plugin_version, true );
201
202 wp_register_style( 'wlgf-settings-css', plugin_dir_url(__FILE__) . 'admin/assets/css/settings.css', array( 'wlgf-admin-ui-css' ), $plugin_version, 'all' );
203 wp_register_script( 'wlgf-settings-js', plugin_dir_url(__FILE__) . 'admin/assets/js/settings.js', array( 'jquery' ), $plugin_version, true );
204
205 wp_register_script( 'wlgf-chartjs', plugin_dir_url(__FILE__) . 'admin/assets/js/chart.umd.min.js', array(), '4.4.1', true );
206 wp_register_script( 'wlgf-dashboard-js', plugin_dir_url(__FILE__) . 'admin/assets/js/dashboard.js', array( 'jquery', 'wlgf-chartjs' ), $plugin_version, true );
207
208 // Enqueue directly in document <head> on plugin admin pages to prevent FOUC / delayed styles
209 if ( $is_wlgf_page ) {
210 wp_enqueue_script( 'jquery' );
211 wp_enqueue_style( 'wlgf-admin-ui-css' );
212 }
213 }
214 add_action( 'admin_enqueue_scripts', 'wlgf_admin_register_scripts' );
215
216 // menu
217 function wlgf_menus() {
218 // add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
219 add_menu_page(
220 'Lead Generation Form',
221 'Lead Generation Form',
222 'manage_options',
223 'lead-generation-form',
224 'wlgf_main',
225 'dashicons-media-spreadsheet',
226 65
227 );
228
229 //add_submenu_page( string $parent_slug, string $page_title, string $menu_title, string $capability, string $menu_slug, callable $function = '', int $position )
230 add_submenu_page( 'lead-generation-form', 'Manage Forms', __('Manage Forms', 'lead-generation-form'), 'manage_options', 'wlgf-manage-forms', 'wlgf_manage_forms' );
231 add_submenu_page( 'lead-generation-form', 'Form Generator', __('Form Generator', 'lead-generation-form'), 'manage_options', 'wlgf-form-generator', 'wlgf_form_generator' );
232 add_submenu_page( 'lead-generation-form', 'All Captured Leads', __('All Captured Leads', 'lead-generation-form'), 'manage_options', 'wlgf-leads', 'wlgf_leads' );
233 add_submenu_page( 'lead-generation-form', 'Import & Export Forms', __('Import & Export', 'lead-generation-form'), 'manage_options', 'wlgf-import-export', 'wlgf_import_export' );
234 add_submenu_page( 'lead-generation-form', 'Settings', __('Settings', 'lead-generation-form'), 'manage_options', 'wlgf-settings', 'wlgf_settings' );
235 }
236 add_action( 'admin_menu', 'wlgf_menus' );
237
238 include('includes/shortcode-ajax.php');
239
240 // main page
241 function wlgf_main(){
242 require 'admin/dashboard.php';
243 }
244
245 // manage forms page
246 function wlgf_manage_forms(){
247 require 'admin/manage-forms.php';
248 }
249
250 // form generator page
251 function wlgf_form_generator(){
252 require 'admin/form-generator.php';
253 }
254
255 // all leads page
256 function wlgf_leads(){
257 require 'admin/leads.php';
258 }
259
260 // import export page
261 function wlgf_import_export(){
262 require 'admin/import-export.php';
263 }
264
265 // settings page
266 function wlgf_settings(){
267 require 'admin/settings.php';
268 }
269
270 function wlgf_get_next_id() {
271 global $wpdb;
272 $all_options = $wpdb->get_col(
273 $wpdb->prepare(
274 "SELECT option_name FROM `$wpdb->options` WHERE `option_name` LIKE %s AND `option_name` NOT LIKE %s",
275 $wpdb->esc_like( 'wlgf_form_' ) . '%',
276 $wpdb->esc_like( 'wlgf_form_views_' ) . '%'
277 )
278 );
279
280 $max_id = 0;
281 if ( ! empty( $all_options ) ) {
282 foreach ( $all_options as $option_name ) {
283 $id = (int) substr( $option_name, strrpos( $option_name, '_' ) + 1 );
284 if ( $id > $max_id ) {
285 $max_id = $id;
286 }
287 }
288 }
289
290 $next_id = $max_id + 1;
291 while ( false !== get_option( 'wlgf_form_' . $next_id ) || false !== get_option( 'wlgf_saved_form_data_' . $next_id ) ) {
292 $next_id++;
293 }
294
295 return $next_id;
296 }
297
298 // 1. manage form actions start
299 function wlgf_manage_form_action_callback(){
300 if ( current_user_can( 'manage_options' ) ) {
301 $wlgf_do_action = isset( $_POST['do'] ) ? sanitize_text_field( wp_unslash( $_POST['do'] ) ) : 'none';
302
303 // clone form start
304 if ( $wlgf_do_action === 'clone' ) {
305 if ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash ( $_POST['nonce'] ) ), 'wlgf-manage-forms' ) ) {
306 if ( isset( $_POST['wlgf_form_id'] ) ) {
307 $wlgf_form_id = sanitize_text_field( wp_unslash( $_POST['wlgf_form_id'] ) );
308 $wlgf_form_clone_data = get_option( 'wlgf_form_' . $wlgf_form_id );
309
310 if ( empty( $wlgf_form_clone_data ) || ! is_array( $wlgf_form_clone_data ) ) {
311 wp_die();
312 }
313
314 $wlgf_form_id_new = wlgf_get_next_id();
315 $wlgf_form_name_new = sanitize_text_field( $wlgf_form_clone_data['form_name'] . ' - Clone' );
316 $wlgf_form_new = isset( $wlgf_form_clone_data['form'] ) ? $wlgf_form_clone_data['form'] : '';
317
318 $wlgf_form_new_details = array(
319 'form_id' => $wlgf_form_id_new,
320 'form_name' => $wlgf_form_name_new,
321 'form' => $wlgf_form_new
322 );
323 update_option( 'wlgf_form_' . $wlgf_form_id_new, $wlgf_form_new_details );
324 delete_transient( 'wlgf_elementor_forms_list' );
325 delete_transient( 'wlgf_gutenberg_forms_list' );
326 ?>
327 <tr id="<?php echo esc_attr( $wlgf_form_id_new ); ?>">
328 <td>
329 <span class="wlgf-badge wlgf-badge--muted">#<?php echo esc_html( $wlgf_form_id_new ); ?></span>
330 </td>
331 <td>
332 <strong><?php echo esc_html( $wlgf_form_name_new ); ?></strong>
333 </td>
334 <td>
335 <span class="wlgf-badge wlgf-badge--muted"><i class="fa-regular fa-eye"></i> 0</span>
336 </td>
337 <td>
338 <span class="wlgf-badge wlgf-badge--muted"><i class="fa-solid fa-users"></i> 0</span>
339 </td>
340 <td>
341 <span class="wlgf-badge wlgf-badge--muted"><i class="fa-solid fa-chart-line"></i> 0%</span>
342 </td>
343 <td>
344 <div class="wlgf-code-pill">
345 <code class="wlgf-code-pill__text">[WLFG id="<?php echo esc_attr( $wlgf_form_id_new ); ?>"]</code>
346 <button type="button" class="wlgf-code-pill__copy-btn" onclick="return WLGF('<?php echo esc_attr( $wlgf_form_id_new ); ?>', 'copy');" title="<?php esc_attr_e( 'Copy Shortcode', 'lead-generation-form' ); ?>">
347 <i class="fa-regular fa-copy"></i>
348 </button>
349 <span id="wlgf-copied-<?php echo esc_attr( $wlgf_form_id_new ); ?>" class="wlgf-code-pill__copied-tooltip"><?php esc_html_e( 'Copied!', 'lead-generation-form' ); ?></span>
350 </div>
351 </td>
352 <td>
353 <div class="wlgf-btn-group">
354 <button type="button" onclick="return WLGF('<?php echo esc_attr( $wlgf_form_id_new ); ?>', 'clone');" class="wlgf-btn wlgf-btn--sm wlgf-btn--outline" title="<?php esc_attr_e( 'Clone Form', 'lead-generation-form' ); ?>">
355 <i class="fa-regular fa-clone"></i> <?php esc_html_e( 'Clone', 'lead-generation-form' ); ?>
356 </button>
357 <a href="<?php echo esc_url( admin_url( 'admin.php?page=wlgf-form-generator&form-id=' . $wlgf_form_id_new . '&modify-nonce=' . wp_create_nonce( 'wlgf-modify-form' ) ) ); ?>" class="wlgf-btn wlgf-btn--sm wlgf-btn--primary" title="<?php esc_attr_e( 'Edit Form', 'lead-generation-form' ); ?>">
358 <i class="fa-solid fa-pen-to-square"></i> <?php esc_html_e( 'Edit', 'lead-generation-form' ); ?>
359 </a>
360 <button type="button" onclick="return WLGF('<?php echo esc_attr( $wlgf_form_id_new ); ?>', 'delete');" class="wlgf-btn wlgf-btn--sm wlgf-btn--danger" title="<?php esc_attr_e( 'Delete Form', 'lead-generation-form' ); ?>">
361 <i class="fa-regular fa-trash-can"></i> <?php esc_html_e( 'Delete', 'lead-generation-form' ); ?>
362 </button>
363 </div>
364 </td>
365 </tr>
366 <?php
367 }
368 wp_die();
369 } else {
370 echo esc_html__( 'Nonce verification failed.', 'lead-generation-form' );
371 wp_die();
372 }
373 } // clone form end
374
375 // delete form start
376 if ( $wlgf_do_action === 'delete' ) {
377 if ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash ( $_POST['nonce'] ) ), 'wlgf-manage-forms' ) ) {
378 if ( isset( $_POST['wlgf_form_id'] ) ) {
379 $wlgf_form_id = sanitize_text_field( wp_unslash( $_POST['wlgf_form_id'] ) );
380
381 delete_option( 'wlgf_form_' . $wlgf_form_id );
382 delete_option( 'wlgf_saved_form_data_' . $wlgf_form_id );
383 delete_transient( 'wlgf_elementor_forms_list' );
384 delete_transient( 'wlgf_gutenberg_forms_list' );
385 }
386 wp_die();
387 } else {
388 echo esc_html__( 'Nonce verification failed.', 'lead-generation-form' );
389 wp_die();
390 }
391 } // delete form end
392 }
393 }
394 add_action( 'wp_ajax_wlgf_manage_form_action', 'wlgf_manage_form_action_callback' );
395 // 1. manage form actions end
396
397
398 // 2. form generator page action start
399 function wlgf_form_save_callback(){
400 if ( current_user_can( 'manage_options' ) ) {
401 $wlgf_is_save = ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-save-form' ) );
402 $wlgf_is_modify = ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-modify-form' ) );
403
404 if ( $wlgf_is_save || $wlgf_is_modify ) {
405 $wlgf_form_id = isset( $_POST['id'] ) ? sanitize_text_field( wp_unslash( $_POST['id'] ) ) : '';
406 $wlgf_form_name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
407 $raw_form_json = isset( $_POST['form'] ) ? wp_unslash( $_POST['form'] ) : '';
408
409 // Safely decode JSON without wp_kses_post corrupting quote boundaries or JSON syntax
410 $wlgf_form = json_decode( $raw_form_json );
411 if ( null === $wlgf_form && ! empty( $raw_form_json ) ) {
412 $wlgf_form = json_decode( stripslashes( $raw_form_json ) );
413 }
414 if ( is_string( $wlgf_form ) ) {
415 $second = json_decode( $wlgf_form );
416 if ( null !== $second ) {
417 $wlgf_form = $second;
418 }
419 }
420
421 // Sanitize form field attributes safely after JSON decoding
422 if ( is_array( $wlgf_form ) ) {
423 foreach ( $wlgf_form as &$field_obj ) {
424 if ( is_object( $field_obj ) ) {
425 if ( isset( $field_obj->label ) ) {
426 $field_obj->label = wp_kses_post( $field_obj->label );
427 }
428 if ( isset( $field_obj->description ) ) {
429 $field_obj->description = wp_kses_post( $field_obj->description );
430 }
431 if ( isset( $field_obj->placeholder ) ) {
432 $field_obj->placeholder = sanitize_text_field( $field_obj->placeholder );
433 }
434 } elseif ( is_array( $field_obj ) ) {
435 if ( isset( $field_obj['label'] ) ) {
436 $field_obj['label'] = wp_kses_post( $field_obj['label'] );
437 }
438 if ( isset( $field_obj['description'] ) ) {
439 $field_obj['description'] = wp_kses_post( $field_obj['description'] );
440 }
441 if ( isset( $field_obj['placeholder'] ) ) {
442 $field_obj['placeholder'] = sanitize_text_field( $field_obj['placeholder'] );
443 }
444 }
445 }
446 unset( $field_obj );
447 }
448
449 // If updating an existing form and incoming canvas failed to decode or was empty, preserve existing structure
450 if ( empty( $wlgf_form ) && ! empty( $wlgf_form_id ) && '0' !== (string) $wlgf_form_id ) {
451 $existing_opt = get_option( 'wlgf_form_' . $wlgf_form_id );
452 if ( ! empty( $existing_opt ) ) {
453 if ( is_array( $existing_opt ) && isset( $existing_opt['form'] ) ) {
454 $wlgf_form = $existing_opt['form'];
455 } elseif ( ! is_array( $existing_opt ) ) {
456 $wlgf_form = $existing_opt;
457 }
458 }
459 }
460
461 if ( empty( $wlgf_form_id ) || '0' === (string) $wlgf_form_id ) {
462 $wlgf_form_id = wlgf_get_next_id();
463 }
464
465 $wlgf_confirmation_type = isset( $_POST['confirmation_type'] ) ? sanitize_text_field( wp_unslash( $_POST['confirmation_type'] ) ) : 'message';
466 $wlgf_redirect_url = isset( $_POST['redirect_url'] ) ? esc_url_raw( wp_unslash( $_POST['redirect_url'] ) ) : '';
467 $wlgf_custom_message = isset( $_POST['custom_message'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_message'] ) ) : '';
468 $wlgf_auto_reply_enabled = isset( $_POST['auto_reply_enabled'] ) ? intval( $_POST['auto_reply_enabled'] ) : 0;
469 $wlgf_auto_reply_subject = isset( $_POST['auto_reply_subject'] ) ? sanitize_text_field( wp_unslash( $_POST['auto_reply_subject'] ) ) : '';
470 $wlgf_auto_reply_body = isset( $_POST['auto_reply_body'] ) ? wp_kses_post( wp_unslash( $_POST['auto_reply_body'] ) ) : '';
471 $wlgf_theme_primary = isset( $_POST['theme_primary'] ) ? sanitize_hex_color( wp_unslash( $_POST['theme_primary'] ) ) : '#2563eb';
472 $wlgf_theme_radius = isset( $_POST['theme_radius'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_radius'] ) ) : '6px';
473 $wlgf_theme_bg = isset( $_POST['theme_bg'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_bg'] ) ) : 'default';
474 $wlgf_theme_font = isset( $_POST['theme_font'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_font'] ) ) : 'inherit';
475 $wlgf_theme_btn_width = isset( $_POST['theme_btn_width'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_btn_width'] ) ) : 'auto';
476 $wlgf_theme_field_size = isset( $_POST['theme_field_size'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_field_size'] ) ) : 'default';
477 $wlgf_theme_mode = isset( $_POST['theme_mode'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_mode'] ) ) : 'light';
478 $wlgf_theme_input_style = isset( $_POST['theme_input_style'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_input_style'] ) ) : 'bordered';
479 $wlgf_theme_max_width = ( isset( $_POST['theme_max_width'] ) && ! empty( $_POST['theme_max_width'] ) ) ? sanitize_text_field( wp_unslash( $_POST['theme_max_width'] ) ) : '640px';
480 $wlgf_theme_btn_shadow = isset( $_POST['theme_btn_shadow'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_btn_shadow'] ) ) : 'default';
481 $wlgf_theme_label_style = isset( $_POST['theme_label_style'] ) ? sanitize_text_field( wp_unslash( $_POST['theme_label_style'] ) ) : 'default';
482 $wlgf_webhook_url = isset( $_POST['webhook_url'] ) ? esc_url_raw( wp_unslash( $_POST['webhook_url'] ) ) : '';
483 $wlgf_webhook_auth = isset( $_POST['webhook_auth'] ) ? sanitize_text_field( wp_unslash( $_POST['webhook_auth'] ) ) : '';
484 $wlgf_schedule_start = isset( $_POST['schedule_start'] ) ? sanitize_text_field( wp_unslash( $_POST['schedule_start'] ) ) : '';
485 $wlgf_schedule_end = isset( $_POST['schedule_end'] ) ? sanitize_text_field( wp_unslash( $_POST['schedule_end'] ) ) : '';
486 $wlgf_max_submissions = isset( $_POST['max_submissions'] ) ? intval( $_POST['max_submissions'] ) : 0;
487 $wlgf_closed_message = isset( $_POST['closed_message'] ) ? sanitize_text_field( wp_unslash( $_POST['closed_message'] ) ) : '';
488 $wlgf_newsletter_enabled = isset( $_POST['newsletter_enabled'] ) ? intval( $_POST['newsletter_enabled'] ) : 0;
489 $wlgf_newsletter_mode = isset( $_POST['newsletter_mode'] ) ? sanitize_text_field( wp_unslash( $_POST['newsletter_mode'] ) ) : 'explicit';
490 $wlgf_newsletter_label = isset( $_POST['newsletter_label'] ) ? sanitize_text_field( wp_unslash( $_POST['newsletter_label'] ) ) : '';
491 $wlgf_newsletter_default_checked = isset( $_POST['newsletter_default_checked'] ) ? intval( $_POST['newsletter_default_checked'] ) : 0;
492 $wlgf_newsletter_tag = isset( $_POST['newsletter_tag'] ) ? sanitize_text_field( wp_unslash( $_POST['newsletter_tag'] ) ) : '';
493
494 // Multi-Step Wizard & Conditional Logic (Phase 2 / PRO-001 & PRO-002)
495 $raw_wizard_style = isset( $_POST['wizard_style'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_style'] ) ) : 'circles';
496 $wlgf_wizard_style = in_array( $raw_wizard_style, array( 'circles', 'bar', 'breadcrumbs' ), true ) ? $raw_wizard_style : 'circles';
497 $wlgf_wizard_prev_text = isset( $_POST['wizard_prev_text'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_prev_text'] ) ) : __( 'Previous', 'lead-generation-form' );
498 $wlgf_wizard_next_text = isset( $_POST['wizard_next_text'] ) ? sanitize_text_field( wp_unslash( $_POST['wizard_next_text'] ) ) : __( 'Next Step', 'lead-generation-form' );
499
500 // Payments Configuration (Pro only - always 0 in Free)
501 $wlgf_payment_enabled = 0;
502 $wlgf_payment_type = isset( $_POST['payment_type'] ) && in_array( $_POST['payment_type'], array( 'fixed', 'calculation' ), true ) ? sanitize_text_field( wp_unslash( $_POST['payment_type'] ) ) : 'fixed';
503 $wlgf_payment_amount = isset( $_POST['payment_amount'] ) ? sanitize_text_field( wp_unslash( $_POST['payment_amount'] ) ) : '0.00';
504 $wlgf_payment_calc_field = isset( $_POST['payment_calc_field'] ) ? sanitize_text_field( wp_unslash( $_POST['payment_calc_field'] ) ) : '';
505 $wlgf_payment_item_name = isset( $_POST['payment_item_name'] ) ? sanitize_text_field( wp_unslash( $_POST['payment_item_name'] ) ) : '';
506
507 $sanitized_conditions = array();
508 $allowed_operators = array( 'equals', 'not_equals', 'contains', 'not_contains', 'greater_than', 'less_than', 'is_empty', 'is_not_empty' );
509 $allowed_actions = array( 'show', 'hide' );
510 $allowed_logics = array( 'all', 'any' );
511
512 if ( isset( $_POST['conditions'] ) && is_array( $_POST['conditions'] ) ) {
513 foreach ( $_POST['conditions'] as $target_field => $cond_data ) {
514 $target_name = sanitize_text_field( $target_field );
515 if ( empty( $target_name ) || ! is_array( $cond_data ) ) {
516 continue;
517 }
518
519 $action = ( isset( $cond_data['action'] ) && in_array( $cond_data['action'], $allowed_actions, true ) ) ? $cond_data['action'] : 'show';
520 $logic = ( isset( $cond_data['logic'] ) && in_array( $cond_data['logic'], $allowed_logics, true ) ) ? $cond_data['logic'] : 'all';
521 $rules = array();
522
523 if ( isset( $cond_data['rules'] ) && is_array( $cond_data['rules'] ) ) {
524 foreach ( $cond_data['rules'] as $rule ) {
525 if ( ! is_array( $rule ) ) {
526 continue;
527 }
528 $source_field = isset( $rule['field'] ) ? sanitize_text_field( $rule['field'] ) : '';
529 $operator = ( isset( $rule['operator'] ) && in_array( $rule['operator'], $allowed_operators, true ) ) ? $rule['operator'] : 'equals';
530 $value = isset( $rule['value'] ) ? sanitize_text_field( $rule['value'] ) : '';
531
532 if ( ! empty( $source_field ) ) {
533 $rules[] = array(
534 'field' => $source_field,
535 'operator' => $operator,
536 'value' => $value,
537 );
538 }
539 }
540 }
541
542 if ( ! empty( $rules ) ) {
543 $sanitized_conditions[ $target_name ] = array(
544 'action' => $action,
545 'logic' => $logic,
546 'rules' => $rules,
547 );
548 }
549 }
550 }
551
552 $wlgf_form_details = array(
553 'form_id' => $wlgf_form_id,
554 'form_name' => $wlgf_form_name,
555 'form' => $wlgf_form,
556 'confirmation_type' => $wlgf_confirmation_type,
557 'redirect_url' => $wlgf_redirect_url,
558 'custom_message' => $wlgf_custom_message,
559 'auto_reply_enabled' => $wlgf_auto_reply_enabled,
560 'auto_reply_subject' => $wlgf_auto_reply_subject,
561 'auto_reply_body' => $wlgf_auto_reply_body,
562 'theme_primary' => $wlgf_theme_primary ? $wlgf_theme_primary : '#2563eb',
563 'theme_radius' => $wlgf_theme_radius,
564 'theme_bg' => $wlgf_theme_bg,
565 'theme_font' => $wlgf_theme_font,
566 'theme_btn_width' => $wlgf_theme_btn_width,
567 'theme_field_size' => $wlgf_theme_field_size,
568 'theme_mode' => $wlgf_theme_mode,
569 'theme_input_style' => $wlgf_theme_input_style,
570 'theme_max_width' => $wlgf_theme_max_width,
571 'theme_btn_shadow' => $wlgf_theme_btn_shadow,
572 'theme_label_style' => $wlgf_theme_label_style,
573 'webhook_url' => $wlgf_webhook_url,
574 'webhook_auth' => $wlgf_webhook_auth,
575 'schedule_start' => $wlgf_schedule_start,
576 'schedule_end' => $wlgf_schedule_end,
577 'max_submissions' => $wlgf_max_submissions,
578 'closed_message' => $wlgf_closed_message,
579 'newsletter_enabled' => $wlgf_newsletter_enabled,
580 'newsletter_mode' => $wlgf_newsletter_mode,
581 'newsletter_label' => $wlgf_newsletter_label,
582 'newsletter_default_checked' => $wlgf_newsletter_default_checked,
583 'newsletter_tag' => $wlgf_newsletter_tag,
584 'wizard_style' => $wlgf_wizard_style,
585 'wizard_prev_text' => $wlgf_wizard_prev_text,
586 'wizard_next_text' => $wlgf_wizard_next_text,
587 'conditions' => $sanitized_conditions,
588 'payment_enabled' => $wlgf_payment_enabled,
589 'payment_type' => $wlgf_payment_type,
590 'payment_amount' => $wlgf_payment_amount,
591 'payment_calc_field' => $wlgf_payment_calc_field,
592 'payment_item_name' => $wlgf_payment_item_name,
593 );
594 update_option( 'wlgf_form_' . $wlgf_form_id, $wlgf_form_details );
595 delete_transient( 'wlgf_elementor_forms_list' );
596 delete_transient( 'wlgf_gutenberg_forms_list' );
597 ?>
598 <div class="wlgf-alert wlgf-alert--success wlgf-mb-3">
599 <i class="fa-solid fa-circle-check"></i>
600 <span><?php esc_html_e( 'Form saved successfully! You can copy the shortcode below or continue updating.', 'lead-generation-form' ); ?></span>
601 </div>
602 <div class="wlgf-flex wlgf-gap-3 wlgf-mb-3">
603 <strong><?php esc_html_e( 'Shortcode:', 'lead-generation-form' ); ?></strong>
604 <div class="wlgf-code-pill">
605 <code class="wlgf-code-pill__text">[WLFG id="<?php echo esc_attr( $wlgf_form_id ); ?>"]</code>
606 <button type="button" class="wlgf-code-pill__copy-btn" onclick="return WLGF('<?php echo esc_attr( $wlgf_form_id ); ?>', 'copy');" title="<?php esc_attr_e( 'Copy Shortcode', 'lead-generation-form' ); ?>">
607 <i class="fa-regular fa-copy"></i>
608 </button>
609 <span id="wlgf-copied-<?php echo esc_attr( $wlgf_form_id ); ?>" class="wlgf-code-pill__copied-tooltip"><?php esc_html_e( 'Copied!', 'lead-generation-form' ); ?></span>
610 </div>
611 </div>
612 <input type="hidden" id="wlgf-modify-form-id" value="<?php echo esc_attr( $wlgf_form_id ); ?>">
613 <button type="button" id="wlgf-modify-form" value="wlgf_modify_form" class="wlgf-action-button wlgf-btn wlgf-btn--primary wlgf-btn--lg">
614 <i class="fa-solid fa-file-pen"></i> <?php esc_html_e( 'Update Form', 'lead-generation-form' ); ?>
615 </button>
616 <?php
617 } else {
618 echo '<div class="wlgf-alert wlgf-alert--danger">' . esc_html__( 'Security check failed. Please refresh and try again.', 'lead-generation-form' ) . '</div>';
619 }
620 wp_die();
621 }
622 }
623 add_action( 'wp_ajax_wlgf_save_form', 'wlgf_form_save_callback' );
624 // 2. form generator page action end
625
626
627 // 3. all leads page action start - dynamic stream and manage leads
628 function wlgf_lead_loader_callback(){
629 if ( current_user_can( 'manage_options' ) ) {
630 if ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-leads' ) ) {
631 if ( isset( $_POST['wlgf_form_id'] ) && isset( $_POST['do'] ) ) {
632 $wlgf_form_id = sanitize_text_field( wp_unslash( $_POST['wlgf_form_id'] ) );
633 $wlgf_do = sanitize_text_field( wp_unslash( $_POST['do'] ) );
634
635 // Load table data
636 if ( $wlgf_do === 'load' ) {
637 $wlgf_form_body = get_option( 'wlgf_form_' . $wlgf_form_id );
638 $wlgf_form = isset( $wlgf_form_body['form'] ) ? $wlgf_form_body['form'] : '';
639 $structure = is_array( $wlgf_form ) ? $wlgf_form : ( is_string( $wlgf_form ) ? json_decode( $wlgf_form ) : array() );
640
641 $wlgf_form_data = get_option( 'wlgf_saved_form_data_' . $wlgf_form_id );
642 $wlgf_newsletter_only = ( isset( $_POST['newsletter_only'] ) && '1' === (string) $_POST['newsletter_only'] );
643 $wlgf_download_url = admin_url( 'admin-ajax.php?action=wlgf_export_leads_csv&wlgf_form_id=' . $wlgf_form_id . '&nonce=' . wp_create_nonce( 'wlgf-export-csv-' . $wlgf_form_id ) );
644 $wlgf_download_subscribers_url = admin_url( 'admin-ajax.php?action=wlgf_export_leads_csv&wlgf_form_id=' . $wlgf_form_id . '&newsletter_only=1&nonce=' . wp_create_nonce( 'wlgf-export-csv-' . $wlgf_form_id ) );
645
646 $wlgf_display_columns = array();
647 if ( is_array( $structure ) ) {
648 foreach ( $structure as $item ) {
649 $item_type = is_object( $item ) ? ( $item->type ?? '' ) : ( $item['type'] ?? '' );
650 $item_name = is_object( $item ) ? ( $item->name ?? '' ) : ( $item['name'] ?? '' );
651 $item_label = is_object( $item ) ? ( $item->label ?? '' ) : ( $item['label'] ?? '' );
652 if ( ! empty( $item_label ) && ! in_array( $item_type, array( 'header', 'button', 'paragraph', 'hidden', 'divider', 'step-break', 'file' ), true ) ) {
653 $wlgf_display_columns[] = array(
654 'name' => $item_name,
655 'label' => $item_label,
656 'type' => $item_type,
657 );
658 }
659 }
660 foreach ( $structure as $item ) {
661 $item_type = is_object( $item ) ? ( $item->type ?? '' ) : ( $item['type'] ?? '' );
662 $item_name = is_object( $item ) ? ( $item->name ?? '' ) : ( $item['name'] ?? '' );
663 $item_label = is_object( $item ) ? ( $item->label ?? '' ) : ( $item['label'] ?? '' );
664 if ( ! empty( $item_label ) && in_array( $item_type, array( 'file' ), true ) ) {
665 $wlgf_display_columns[] = array(
666 'name' => $item_name,
667 'label' => $item_label,
668 'type' => $item_type,
669 );
670 }
671 }
672 }
673 ?>
674 <!-- Leads Filter & Export Toolbar (PRO-008) -->
675 <div class="wlgf-leads-toolbar wlgf-flex wlgf-flex--between wlgf-items-center wlgf-mb-3 wlgf-flex-wrap wlgf-gap-2">
676 <div class="wlgf-btn-group">
677 <button type="button" class="wlgf-btn wlgf-btn--sm <?php echo ! $wlgf_newsletter_only ? 'wlgf-btn--primary' : 'wlgf-btn--outline'; ?> wlgf-filter-leads-btn" data-form-id="<?php echo esc_attr( $wlgf_form_id ); ?>" data-newsletter-only="0">
678 <i class="fa-solid fa-users"></i> <?php esc_html_e( 'All Captured Leads', 'lead-generation-form' ); ?>
679 </button>
680 <button type="button" class="wlgf-btn wlgf-btn--sm <?php echo $wlgf_newsletter_only ? 'wlgf-btn--primary' : 'wlgf-btn--outline'; ?> wlgf-filter-leads-btn" data-form-id="<?php echo esc_attr( $wlgf_form_id ); ?>" data-newsletter-only="1">
681 <i class="fa-solid fa-paper-plane"></i> <?php esc_html_e( 'Newsletter Subscribers Only', 'lead-generation-form' ); ?>
682 </button>
683 </div>
684 <div class="wlgf-flex wlgf-items-center wlgf-gap-2">
685 <button type="button" id="wlgf-delete-all" class="wlgf-btn wlgf-btn--sm wlgf-btn--danger" onclick="return WLGF('<?php echo esc_attr( $wlgf_form_id ); ?>', 'multiple');" title="<?php esc_attr_e( 'Delete Selected Leads', 'lead-generation-form' ); ?>">
686 <i class="fa-regular fa-trash-can"></i> <?php esc_html_e( 'Delete Selected', 'lead-generation-form' ); ?>
687 </button>
688 <a href="<?php echo esc_url( $wlgf_download_url ); ?>" class="wlgf-btn wlgf-btn--sm wlgf-btn--primary" title="<?php esc_attr_e( 'Export all captured form submissions with complete field data', 'lead-generation-form' ); ?>">
689 <i class="fa-solid fa-file-csv"></i> <?php esc_html_e( 'Export All Leads (CSV)', 'lead-generation-form' ); ?>
690 </a>
691 <a href="<?php echo esc_url( $wlgf_download_subscribers_url ); ?>" class="wlgf-btn wlgf-btn--sm wlgf-btn--outline" title="<?php esc_attr_e( 'Export only contacts opted-in to the newsletter', 'lead-generation-form' ); ?>">
692 <i class="fa-solid fa-paper-plane"></i> <?php esc_html_e( 'Export Subscribers (CSV)', 'lead-generation-form' ); ?>
693 </a>
694 </div>
695 </div>
696
697 <div class="wlgf-table-wrap">
698 <table id="forms-tables" class="wlgf-table wlgf-w-full">
699 <thead>
700 <tr>
701 <?php foreach ( $wlgf_display_columns as $col ) : ?>
702 <th><?php echo esc_html( $col['label'] ); ?></th>
703 <?php endforeach; ?>
704 <th><?php esc_html_e( 'Date Time', 'lead-generation-form' ); ?></th>
705 <th><?php esc_html_e( 'Newsletter', 'lead-generation-form' ); ?></th>
706 <th><?php esc_html_e( 'Payment', 'lead-generation-form' ); ?></th>
707 <th><?php esc_html_e( 'Status', 'lead-generation-form' ); ?></th>
708 <th><?php esc_html_e( 'Actions', 'lead-generation-form' ); ?></th>
709 <th>
710 <input type="checkbox" id="wlgf-select-all" name="wlgf-select-all" title="<?php esc_attr_e( 'Select All', 'lead-generation-form' ); ?>">
711 </th>
712 </tr>
713 </thead>
714 <tbody>
715 <?php
716 if ( is_array( $wlgf_form_data ) ) {
717 $wlgf_data_counter = 0;
718 $wlgf_trap = 100; // Free version caps at 100 leads
719 foreach ( $wlgf_form_data as $row_key => $row ) {
720 if ( ! is_array( $row ) ) {
721 continue;
722 }
723 if ( $wlgf_newsletter_only && ( empty( $row['_newsletter_optin'] ) || 1 !== (int) $row['_newsletter_optin'] ) ) {
724 continue;
725 }
726 $lead_status = isset( $row['_status'] ) ? sanitize_text_field( $row['_status'] ) : 'new';
727 $lead_notes = isset( $row['_notes'] ) ? $row['_notes'] : array();
728 $notes_count = is_array( $lead_notes ) ? count( $lead_notes ) : 0;
729 $is_optin = ( ! empty( $row['_newsletter_optin'] ) && 1 === (int) $row['_newsletter_optin'] );
730 $nl_status = isset( $row['_newsletter_status'] ) ? sanitize_text_field( $row['_newsletter_status'] ) : '';
731 $nl_prov = isset( $row['_newsletter_provider'] ) ? sanitize_text_field( $row['_newsletter_provider'] ) : '';
732
733 $pay_status = isset( $row['wlgf_payment_status'] ) ? sanitize_text_field( $row['wlgf_payment_status'] ) : '';
734 $pay_tx = isset( $row['wlgf_payment_transaction_id'] ) ? sanitize_text_field( $row['wlgf_payment_transaction_id'] ) : '';
735 $pay_amt = isset( $row['wlgf_payment_amount_paid'] ) ? sanitize_text_field( $row['wlgf_payment_amount_paid'] ) : '';
736 $pay_gw = isset( $row['wlgf_payment_gateway'] ) ? sanitize_text_field( $row['wlgf_payment_gateway'] ) : '';
737 $date_time = isset( $row['Date Time'] ) ? $row['Date Time'] : ( isset( $row['date_time'] ) ? $row['date_time'] : 'N/A' );
738 ?>
739 <tr id="<?php echo esc_attr( $row_key ); ?>">
740 <?php
741 foreach ( $wlgf_display_columns as $col ) {
742 $field_name = $col['name'];
743 $field_label = $col['label'];
744 $field_type = $col['type'];
745
746 // Lookup by field name first, then fallback to field label
747 $val = null;
748 if ( ! empty( $field_name ) && array_key_exists( $field_name, $row ) ) {
749 $val = $row[ $field_name ];
750 } elseif ( ! empty( $field_label ) && array_key_exists( $field_label, $row ) ) {
751 $val = $row[ $field_label ];
752 }
753
754 if ( 'file' === $field_type ) {
755 echo '<td>';
756 if ( ! empty( $val ) ) {
757 $urls = is_array( $val ) ? $val : json_decode( $val, true );
758 if ( is_array( $urls ) ) {
759 echo '<div class="wlgf-flex wlgf-gap-1">';
760 foreach ( $urls as $file_url ) {
761 $escaped_url = esc_url( $file_url );
762 echo '<a href="' . $escaped_url . '" target="_blank" rel="noopener noreferrer"><img src="' . $escaped_url . '" class="wlgf-lead-img-thumb" alt="' . esc_attr__( 'Attachment', 'lead-generation-form' ) . '"></a>';
763 }
764 echo '</div>';
765 } else {
766 $escaped_url = esc_url( $val );
767 echo '<a href="' . $escaped_url . '" target="_blank" rel="noopener noreferrer"><img src="' . $escaped_url . '" class="wlgf-lead-img-thumb" alt="' . esc_attr__( 'Attachment', 'lead-generation-form' ) . '"></a>';
768 }
769 } else {
770 echo '<span class="wlgf-lead-empty-dash">—</span>';
771 }
772 echo '</td>';
773 } elseif ( 'checkbox-group' === $field_type || is_array( $val ) ) {
774 if ( is_array( $val ) ) {
775 $val_str = implode( ' | ', $val );
776 echo '<td>' . esc_html( ! empty( $val_str ) ? $val_str : '' ) . '</td>';
777 } else {
778 echo '<td>' . esc_html( ( null !== $val && '' !== $val ) ? $val : '' ) . '</td>';
779 }
780 } else {
781 echo '<td>' . esc_html( ( null !== $val && '' !== $val ) ? $val : '' ) . '</td>';
782 }
783 }
784 ?>
785 <td>
786 <?php
787 $dt_ts = strtotime( $date_time );
788 if ( $dt_ts ) {
789 echo '<span class="wlgf-lead-date-date">' . esc_html( date_i18n( 'M j, Y', $dt_ts ) ) . '</span>';
790 echo '<div class="wlgf-lead-date-time">' . esc_html( date_i18n( 'g:i a', $dt_ts ) ) . '</div>';
791 } else {
792 echo '<span class="wlgf-lead-date-date">' . esc_html( $date_time ) . '</span>';
793 }
794 ?>
795 </td>
796 <td class="text-center">
797 <?php if ( $is_optin ) : ?>
798 <?php
799 $tooltip = __( 'Opted-in to newsletter', 'lead-generation-form' );
800 if ( ! empty( $nl_status ) ) {
801 $tooltip .= ' (' . ucfirst( $nl_prov ) . ': ' . $nl_status . ')';
802 }
803 ?>
804 <span class="wlgf-badge wlgf-badge--success" title="<?php echo esc_attr( $tooltip ); ?>">
805 <i class="fa-solid fa-check"></i> <?php esc_html_e( 'Yes', 'lead-generation-form' ); ?>
806 </span>
807 <?php else : ?>
808 <span class="wlgf-lead-empty-dash"></span>
809 <?php endif; ?>
810 </td>
811 <td>
812 <?php if ( ! empty( $pay_status ) && 'completed' === $pay_status ) : ?>
813 <span class="wlgf-badge wlgf-badge--success" title="<?php echo esc_attr( 'TX: ' . $pay_tx . ' via ' . ucfirst( $pay_gw ) ); ?>">
814 <i class="fa-solid fa-check"></i> <?php echo esc_html( '$' . $pay_amt . ' ' . ucfirst( $pay_gw ) ); ?>
815 </span>
816 <?php elseif ( ! empty( $pay_status ) && 'authorized' === $pay_status ) : ?>
817 <span class="wlgf-badge wlgf-badge--primary" title="<?php echo esc_attr( 'TX: ' . $pay_tx . ' via ' . ucfirst( $pay_gw ) ); ?>">
818 <i class="fa-solid fa-credit-card"></i> <?php echo esc_html( '$' . $pay_amt . ' ' . ucfirst( $pay_gw ) ); ?>
819 </span>
820 <?php else : ?>
821 <span class="wlgf-lead-empty-dash"></span>
822 <?php endif; ?>
823 </td>
824 <td>
825 <select class="wlgf-status-select" data-form-id="<?php echo esc_attr( $wlgf_form_id ); ?>" data-lead-index="<?php echo esc_attr( $row_key ); ?>">
826 <option value="new" <?php selected( $lead_status, 'new' ); ?>><?php esc_html_e( 'New', 'lead-generation-form' ); ?></option>
827 <option value="review" <?php selected( $lead_status, 'review' ); ?>><?php esc_html_e( 'In Review', 'lead-generation-form' ); ?></option>
828 <option value="contacted" <?php selected( $lead_status, 'contacted' ); ?>><?php esc_html_e( 'Contacted', 'lead-generation-form' ); ?></option>
829 <option value="qualified" <?php selected( $lead_status, 'qualified' ); ?>><?php esc_html_e( 'Qualified', 'lead-generation-form' ); ?></option>
830 <option value="won" <?php selected( $lead_status, 'won' ); ?>><?php esc_html_e( 'Won', 'lead-generation-form' ); ?></option>
831 <option value="lost" <?php selected( $lead_status, 'lost' ); ?>><?php esc_html_e( 'Lost', 'lead-generation-form' ); ?></option>
832 </select>
833 </td>
834 <td>
835 <div class="wlgf-btn-group">
836 <button type="button" class="wlgf-btn wlgf-btn--sm wlgf-btn--outline wlgf-open-notes-btn" data-form-id="<?php echo esc_attr( $wlgf_form_id ); ?>" data-lead-index="<?php echo esc_attr( $row_key ); ?>" data-notes="<?php echo esc_attr( wp_json_encode( $lead_notes ) ); ?>" title="<?php esc_attr_e( 'Internal Notes', 'lead-generation-form' ); ?>">
837 <i class="fa-regular fa-note-sticky"></i> <span class="wlgf-notes-count"><?php echo esc_html( $notes_count ); ?></span>
838 </button>
839 <button onclick="return WLGF('<?php echo esc_attr( $wlgf_form_id ); ?>', 'delete', '<?php echo esc_attr( $row_key ); ?>');" type="button" class="wlgf-btn wlgf-btn--sm wlgf-btn--danger" title="<?php esc_attr_e( 'Delete Entry', 'lead-generation-form' ); ?>">
840 <i class="fa-regular fa-trash-can"></i>
841 </button>
842 </div>
843 </td>
844 <td>
845 <input type="checkbox" name="wlgf-lead-id" value="<?php echo esc_attr( $row_key ); ?>" title="<?php esc_attr_e( 'Select Lead', 'lead-generation-form' ); ?>">
846 </td>
847 </tr>
848 <?php
849 $wlgf_data_counter++;
850 if ( $wlgf_data_counter >= $wlgf_trap ) {
851 break;
852 }
853 }
854 }
855 ?>
856 </tbody>
857 </table>
858 </div>
859 <?php
860 } // end of load if
861
862 // Delete single record
863 if ( $wlgf_do === 'delete' ) {
864 $wlgf_row_id = isset( $_POST['wlgf_row_id'] ) ? sanitize_text_field( wp_unslash( $_POST['wlgf_row_id'] ) ) : '';
865 if ( ! empty( $wlgf_row_id ) ) {
866 $wlgf_saved_form_data = get_option( 'wlgf_saved_form_data_' . $wlgf_form_id );
867 if ( is_array( $wlgf_saved_form_data ) ) {
868 unset( $wlgf_saved_form_data[ $wlgf_row_id ] );
869 update_option( 'wlgf_saved_form_data_' . $wlgf_form_id, $wlgf_saved_form_data );
870 }
871 }
872 }
873
874 // Delete multiple selected records
875 if ( $wlgf_do === 'multiple' ) {
876 if ( isset( $_POST['wlgf_row_id'] ) && is_array( $_POST['wlgf_row_id'] ) ) {
877 $unslashed_row_ids = wp_unslash( $_POST['wlgf_row_id'] );
878 $wlgf_saved_form_data = get_option( 'wlgf_saved_form_data_' . $wlgf_form_id );
879 if ( is_array( $wlgf_saved_form_data ) ) {
880 foreach ( $unslashed_row_ids as $row_id ) {
881 $sanitized_id = sanitize_text_field( $row_id );
882 if ( array_key_exists( $sanitized_id, $wlgf_saved_form_data ) ) {
883 unset( $wlgf_saved_form_data[ $sanitized_id ] );
884 }
885 }
886 update_option( 'wlgf_saved_form_data_' . $wlgf_form_id, $wlgf_saved_form_data );
887 }
888 }
889 }
890 }
891 }
892 wp_die();
893 }
894 }
895 add_action( 'wp_ajax_wlgf_lead_loader', 'wlgf_lead_loader_callback' );
896
897 // 3.01 Mini-CRM Lead Status Update (PRO-011)
898 function wlgf_update_lead_status_callback() {
899 if ( ! current_user_can( 'manage_options' ) ) {
900 wp_send_json_error( array( 'message' => __( 'Unauthorized access.', 'lead-generation-form' ) ) );
901 }
902 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-leads' ) ) {
903 wp_send_json_error( array( 'message' => __( 'Security verification failed.', 'lead-generation-form' ) ) );
904 }
905
906 $form_id = isset( $_POST['form_id'] ) ? sanitize_text_field( wp_unslash( $_POST['form_id'] ) ) : '';
907 $lead_index = isset( $_POST['lead_index'] ) ? intval( $_POST['lead_index'] ) : -1;
908 $new_status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : 'new';
909
910 if ( empty( $form_id ) || $lead_index < 0 ) {
911 wp_send_json_error( array( 'message' => __( 'Invalid parameters.', 'lead-generation-form' ) ) );
912 }
913
914 $leads_data = get_option( 'wlgf_saved_form_data_' . $form_id, array() );
915 if ( isset( $leads_data[ $lead_index ] ) ) {
916 $leads_data[ $lead_index ]['_status'] = $new_status;
917 update_option( 'wlgf_saved_form_data_' . $form_id, $leads_data );
918 wp_send_json_success( array( 'status' => $new_status ) );
919 }
920 wp_send_json_error( array( 'message' => __( 'Lead not found.', 'lead-generation-form' ) ) );
921 }
922 add_action( 'wp_ajax_wlgf_update_lead_status', 'wlgf_update_lead_status_callback' );
923
924 // 3.02 Mini-CRM Internal Notes Handler (PRO-011)
925 function wlgf_add_lead_note_callback() {
926 if ( ! current_user_can( 'manage_options' ) ) {
927 wp_send_json_error( array( 'message' => __( 'Unauthorized access.', 'lead-generation-form' ) ) );
928 }
929 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-leads' ) ) {
930 wp_send_json_error( array( 'message' => __( 'Security verification failed.', 'lead-generation-form' ) ) );
931 }
932
933 $form_id = isset( $_POST['form_id'] ) ? sanitize_text_field( wp_unslash( $_POST['form_id'] ) ) : '';
934 $lead_index = isset( $_POST['lead_index'] ) ? intval( $_POST['lead_index'] ) : -1;
935 $note_text = isset( $_POST['note'] ) ? sanitize_textarea_field( wp_unslash( $_POST['note'] ) ) : '';
936
937 if ( empty( $form_id ) || $lead_index < 0 || empty( $note_text ) ) {
938 wp_send_json_error( array( 'message' => __( 'Invalid parameters or empty note.', 'lead-generation-form' ) ) );
939 }
940
941 $leads_data = get_option( 'wlgf_saved_form_data_' . $form_id, array() );
942 if ( isset( $leads_data[ $lead_index ] ) ) {
943 if ( ! isset( $leads_data[ $lead_index ]['_notes'] ) || ! is_array( $leads_data[ $lead_index ]['_notes'] ) ) {
944 $leads_data[ $lead_index ]['_notes'] = array();
945 }
946 $current_user = wp_get_current_user();
947 $new_note = array(
948 'text' => $note_text,
949 'time' => current_time( 'mysql' ),
950 'user' => $current_user->display_name ? $current_user->display_name : 'Admin',
951 );
952 $leads_data[ $lead_index ]['_notes'][] = $new_note;
953 update_option( 'wlgf_saved_form_data_' . $form_id, $leads_data );
954 wp_send_json_success( array(
955 'notes' => $leads_data[ $lead_index ]['_notes'],
956 'count' => count( $leads_data[ $lead_index ]['_notes'] ),
957 ) );
958 }
959 wp_send_json_error( array( 'message' => __( 'Lead not found.', 'lead-generation-form' ) ) );
960 }
961 add_action( 'wp_ajax_wlgf_add_lead_note', 'wlgf_add_lead_note_callback' );
962
963 // 3.1 Streamed on-demand CSV Export handler
964 function wlgf_export_leads_csv_callback() {
965 if ( ! current_user_can( 'manage_options' ) ) {
966 wp_die( esc_html__( 'Unauthorized access.', 'lead-generation-form' ) );
967 }
968 $wlgf_form_id = isset( $_GET['wlgf_form_id'] ) ? sanitize_text_field( wp_unslash( $_GET['wlgf_form_id'] ) ) : '';
969 if ( empty( $wlgf_form_id ) || ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'wlgf-export-csv-' . $wlgf_form_id ) ) {
970 wp_die( esc_html__( 'Security check failed.', 'lead-generation-form' ) );
971 }
972
973 $wlgf_form_body = get_option( 'wlgf_form_' . $wlgf_form_id );
974 $wlgf_form_name = isset( $wlgf_form_body['form_name'] ) ? $wlgf_form_body['form_name'] : 'form';
975 $wlgf_form = isset( $wlgf_form_body['form'] ) ? $wlgf_form_body['form'] : '';
976 $structure = is_array( $wlgf_form ) ? $wlgf_form : ( is_string( $wlgf_form ) ? json_decode( $wlgf_form ) : array() );
977
978 $wlgf_form_data = get_option( 'wlgf_saved_form_data_' . $wlgf_form_id );
979 $wlgf_newsletter_only = ( isset( $_GET['newsletter_only'] ) && '1' === (string) $_GET['newsletter_only'] );
980
981 if ( ob_get_level() ) {
982 ob_end_clean();
983 }
984
985 $file_suffix = $wlgf_newsletter_only ? '-subscribers-' : '-leads-';
986 $filename = sanitize_file_name( $wlgf_form_name ) . $file_suffix . gmdate( 'Y-m-d' ) . '.csv';
987 header( 'Content-Type: text/csv; charset=UTF-8' );
988 header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
989 header( 'Pragma: no-cache' );
990 header( 'Expires: 0' );
991
992 $output = fopen( 'php://output', 'w' );
993 // UTF-8 BOM for Excel
994 fprintf( $output, chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF ) );
995
996 $wlgf_display_columns = array();
997 if ( is_array( $structure ) ) {
998 foreach ( $structure as $item ) {
999 $item_type = is_object( $item ) ? ( $item->type ?? '' ) : ( $item['type'] ?? '' );
1000 $item_name = is_object( $item ) ? ( $item->name ?? '' ) : ( $item['name'] ?? '' );
1001 $item_label = is_object( $item ) ? ( $item->label ?? '' ) : ( $item['label'] ?? '' );
1002 if ( ! empty( $item_label ) && ! in_array( $item_type, array( 'header', 'button', 'paragraph', 'hidden', 'divider', 'step-break', 'file' ), true ) ) {
1003 $wlgf_display_columns[] = array(
1004 'name' => $item_name,
1005 'label' => $item_label,
1006 'type' => $item_type,
1007 );
1008 }
1009 }
1010 foreach ( $structure as $item ) {
1011 $item_type = is_object( $item ) ? ( $item->type ?? '' ) : ( $item['type'] ?? '' );
1012 $item_name = is_object( $item ) ? ( $item->name ?? '' ) : ( $item['name'] ?? '' );
1013 $item_label = is_object( $item ) ? ( $item->label ?? '' ) : ( $item['label'] ?? '' );
1014 if ( ! empty( $item_label ) && in_array( $item_type, array( 'file' ), true ) ) {
1015 $wlgf_display_columns[] = array(
1016 'name' => $item_name,
1017 'label' => $item_label,
1018 'type' => $item_type,
1019 );
1020 }
1021 }
1022 }
1023
1024 $headers = array();
1025 foreach ( $wlgf_display_columns as $col ) {
1026 $headers[] = $col['label'];
1027 }
1028 $headers[] = __( 'Date Time', 'lead-generation-form' );
1029 $headers[] = __( 'Newsletter Opt-in', 'lead-generation-form' );
1030 $headers[] = __( 'Payment Status', 'lead-generation-form' );
1031 $headers[] = __( 'Transaction ID', 'lead-generation-form' );
1032 $headers[] = __( 'Amount Paid', 'lead-generation-form' );
1033 $headers[] = __( 'Lead Status', 'lead-generation-form' );
1034 fputcsv( $output, $headers );
1035
1036 if ( is_array( $wlgf_form_data ) ) {
1037 $counter = 0;
1038 $max_export = 100; // Free version limit
1039 foreach ( $wlgf_form_data as $row ) {
1040 if ( ! is_array( $row ) ) {
1041 continue;
1042 }
1043 if ( $wlgf_newsletter_only && ( empty( $row['_newsletter_optin'] ) || 1 !== (int) $row['_newsletter_optin'] ) ) {
1044 continue;
1045 }
1046 $csv_row = array();
1047 foreach ( $wlgf_display_columns as $col ) {
1048 $field_name = $col['name'];
1049 $field_label = $col['label'];
1050 $val = null;
1051 if ( ! empty( $field_name ) && array_key_exists( $field_name, $row ) ) {
1052 $val = $row[ $field_name ];
1053 } elseif ( ! empty( $field_label ) && array_key_exists( $field_label, $row ) ) {
1054 $val = $row[ $field_label ];
1055 }
1056 if ( is_array( $val ) ) {
1057 $csv_row[] = implode( ' | ', $val );
1058 } else {
1059 $csv_row[] = ( null !== $val && '' !== $val ) ? $val : '';
1060 }
1061 }
1062 $csv_row[] = isset( $row['Date Time'] ) ? $row['Date Time'] : ( isset( $row['date_time'] ) ? $row['date_time'] : '' );
1063 $is_optin = ( ! empty( $row['_newsletter_optin'] ) && 1 === (int) $row['_newsletter_optin'] );
1064 $csv_row[] = $is_optin ? __( 'Yes (Opted-in)', 'lead-generation-form' ) : __( 'No', 'lead-generation-form' );
1065 $csv_row[] = ! empty( $row['wlgf_payment_status'] ) ? $row['wlgf_payment_status'] : 'N/A';
1066 $csv_row[] = ! empty( $row['wlgf_payment_transaction_id'] ) ? $row['wlgf_payment_transaction_id'] : '';
1067 $csv_row[] = ! empty( $row['wlgf_payment_amount_paid'] ) ? $row['wlgf_payment_amount_paid'] : '';
1068 $csv_row[] = isset( $row['_status'] ) ? $row['_status'] : 'new';
1069
1070 $escaped_csv_row = array();
1071 foreach ( $csv_row as $cell ) {
1072 $cell_str = is_scalar( $cell ) ? (string) $cell : '';
1073 if ( preg_match( '/^[=+\-@\t\r]/', $cell_str ) ) {
1074 $cell_str = "'" . $cell_str;
1075 }
1076 $escaped_csv_row[] = $cell_str;
1077 }
1078 fputcsv( $output, $escaped_csv_row );
1079 $counter++;
1080 if ( $counter >= $max_export ) {
1081 break;
1082 }
1083 }
1084 }
1085
1086 fclose( $output );
1087 exit;
1088 }
1089 add_action( 'wp_ajax_wlgf_export_leads_csv', 'wlgf_export_leads_csv_callback' );
1090 // 3. all leads page action end - load + file, delete, multiple delete [end]
1091
1092 // 4. import export form start
1093 function wlgf_import_export_callback(){
1094 if ( current_user_can( 'manage_options' ) ) {
1095 if ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-import-export' ) ) {
1096 $wlgf_do_action = isset( $_POST['do'] ) ? sanitize_text_field( wp_unslash( $_POST['do'] ) ) : '';
1097
1098 // Export Form
1099 if ( $wlgf_do_action === 'export' ) {
1100 $wlgf_form_id = isset( $_POST['wlgf_form_id'] ) ? sanitize_text_field( wp_unslash( $_POST['wlgf_form_id'] ) ) : '';
1101 $wlgf_form_details = get_option( 'wlgf_form_' . $wlgf_form_id );
1102 if ( ! empty( $wlgf_form_details ) && isset( $wlgf_form_details['form'] ) ) {
1103 echo '<textarea id="wlgf-form-code" class="wlgf-code-box" rows="7" readonly>' . esc_textarea( $wlgf_form_details['form'] ) . '</textarea>';
1104 } else {
1105 echo '<div class="wlgf-alert wlgf-alert--danger">' . esc_html__( 'Error: Form data not found.', 'lead-generation-form' ) . '</div>';
1106 }
1107 wp_die();
1108 }
1109
1110 // Import Form
1111 if ( $wlgf_do_action === 'import' ) {
1112 $wlgf_form_name = isset( $_POST['wlgf_form_name'] ) ? sanitize_text_field( wp_unslash( $_POST['wlgf_form_name'] ) ) : '';
1113 $raw_json = isset( $_POST['wlgf_form_data'] ) ? wp_unslash( $_POST['wlgf_form_data'] ) : '';
1114 $decoded = json_decode( $raw_json, true );
1115
1116 if ( empty( $decoded ) || ! is_array( $decoded ) ) {
1117 wp_send_json_error( array( 'message' => __( 'Invalid JSON format. Please paste valid form schema JSON.', 'lead-generation-form' ) ) );
1118 }
1119
1120 $allowed_types = array( 'text', 'number', 'date', 'file', 'textarea', 'select', 'checkbox-group', 'radio-group', 'header', 'paragraph', 'hidden', 'button', 'autocomplete', 'step-break', 'star-rating', 'nps-matrix', 'calculation', 'divider', 'terms' );
1121 $valid_fields = array();
1122 foreach ( $decoded as $field ) {
1123 if ( is_array( $field ) && isset( $field['type'] ) && in_array( $field['type'], $allowed_types, true ) ) {
1124 $valid_fields[] = $field;
1125 }
1126 }
1127
1128 if ( empty( $valid_fields ) ) {
1129 wp_send_json_error( array( 'message' => __( 'No valid form fields found in the imported JSON payload.', 'lead-generation-form' ) ) );
1130 }
1131
1132 $wlgf_form_id = wlgf_get_next_id();
1133 $wlgf_form_details = array(
1134 'form_id' => $wlgf_form_id,
1135 'form_name' => $wlgf_form_name ? $wlgf_form_name : ( 'Imported Form #' . $wlgf_form_id ),
1136 'form' => wp_json_encode( $valid_fields )
1137 );
1138
1139 update_option( 'wlgf_form_' . $wlgf_form_id, $wlgf_form_details );
1140 wp_send_json_success( array( 'message' => sprintf( __( 'Form "%s" (ID: #%s) imported successfully!', 'lead-generation-form' ), esc_html( $wlgf_form_details['form_name'] ), esc_html( $wlgf_form_id ) ) ) );
1141 }
1142
1143 // Combine Forms
1144 if ( $wlgf_do_action === 'combine' ) {
1145 $wlgf_form_one_id = isset( $_POST['wlgf_form_one_id'] ) ? sanitize_text_field( wp_unslash( $_POST['wlgf_form_one_id'] ) ) : '';
1146 $wlgf_form_two_id = isset( $_POST['wlgf_form_two_id'] ) ? sanitize_text_field( wp_unslash( $_POST['wlgf_form_two_id'] ) ) : '';
1147
1148 $wlgf_form_one = get_option( 'wlgf_form_' . $wlgf_form_one_id );
1149 $wlgf_form_two = get_option( 'wlgf_form_' . $wlgf_form_two_id );
1150
1151 if ( empty( $wlgf_form_one ) || empty( $wlgf_form_two ) ) {
1152 wp_send_json_error( array( 'message' => __( 'Please select two valid forms to combine.', 'lead-generation-form' ) ) );
1153 }
1154
1155 $raw_one = isset( $wlgf_form_one['form'] ) ? $wlgf_form_one['form'] : array();
1156 $raw_two = isset( $wlgf_form_two['form'] ) ? $wlgf_form_two['form'] : array();
1157 $form_one_data = is_array( $raw_one ) ? $raw_one : ( is_string( $raw_one ) ? json_decode( $raw_one, true ) : array() );
1158 $form_two_data = is_array( $raw_two ) ? $raw_two : ( is_string( $raw_two ) ? json_decode( $raw_two, true ) : array() );
1159
1160 if ( ! is_array( $form_one_data ) ) { $form_one_data = array(); }
1161 if ( ! is_array( $form_two_data ) ) { $form_two_data = array(); }
1162
1163 $combined_data = array_merge( $form_one_data, $form_two_data );
1164 $wlgf_form_id_new = wlgf_get_next_id();
1165 $wlgf_form_name_new = sanitize_text_field( $wlgf_form_one['form_name'] . ' & ' . $wlgf_form_two['form_name'] . ' - [Combined]' );
1166
1167 $wlgf_form_details_new = array(
1168 'form_id' => $wlgf_form_id_new,
1169 'form_name' => $wlgf_form_name_new,
1170 'form' => wp_json_encode( $combined_data )
1171 );
1172
1173 update_option( 'wlgf_form_' . $wlgf_form_id_new, $wlgf_form_details_new );
1174 wp_send_json_success( array( 'message' => sprintf( __( 'Combined form "%s" (ID: #%s) created successfully!', 'lead-generation-form' ), esc_html( $wlgf_form_name_new ), esc_html( $wlgf_form_id_new ) ) ) );
1175 }
1176 }
1177 wp_die();
1178 }
1179 }
1180 add_action( 'wp_ajax_wlgf_import_export', 'wlgf_import_export_callback' );
1181 // 4. import export form end
1182
1183 // 5. save settings start
1184 function wlgf_save_settings_callback(){
1185 if ( current_user_can( 'manage_options' ) ) {
1186 if ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-save-settings' ) ) {
1187 $existing_settings = get_option( 'wlgf_settings', array() );
1188
1189 $wlgf_recaptcha = isset( $_POST['recaptcha'] ) ? sanitize_text_field( wp_unslash( $_POST['recaptcha'] ) ) : '2';
1190 $wlgf_sitekey = isset( $_POST['sitekey'] ) ? sanitize_text_field( wp_unslash( $_POST['sitekey'] ) ) : '';
1191 $wlgf_secretkey = isset( $_POST['secretkey'] ) ? sanitize_text_field( wp_unslash( $_POST['secretkey'] ) ) : '';
1192 if ( ( empty( $wlgf_secretkey ) || '••••••••••••' === $wlgf_secretkey ) && ! empty( $existing_settings['secretkey'] ) ) {
1193 $wlgf_secretkey = $existing_settings['secretkey'];
1194 }
1195
1196 $wlgf_notify_admin = isset( $_POST['notify_admin'] ) ? sanitize_text_field( wp_unslash( $_POST['notify_admin'] ) ) : '2';
1197 $wlgf_email_engine = isset( $_POST['email_engine'] ) ? sanitize_text_field( wp_unslash( $_POST['email_engine'] ) ) : '1';
1198 $wlgf_smtp_host = isset( $_POST['smtp_host'] ) ? sanitize_text_field( wp_unslash( $_POST['smtp_host'] ) ) : '';
1199 $wlgf_smtp_username = isset( $_POST['smtp_username'] ) ? sanitize_text_field( wp_unslash( $_POST['smtp_username'] ) ) : '';
1200
1201 // SMTP Password preservation rule (R5)
1202 $wlgf_smtp_password = isset( $_POST['smtp_password'] ) ? sanitize_text_field( wp_unslash( $_POST['smtp_password'] ) ) : '';
1203 if ( empty( $wlgf_smtp_password ) && ! empty( $existing_settings['smtp_password'] ) ) {
1204 $wlgf_smtp_password = $existing_settings['smtp_password'];
1205 }
1206
1207 $wlgf_smtp_encryption = isset( $_POST['smtp_encryption'] ) ? sanitize_text_field( wp_unslash( $_POST['smtp_encryption'] ) ) : '1';
1208 $wlgf_smtp_port = isset( $_POST['smtp_port'] ) ? sanitize_text_field( wp_unslash( $_POST['smtp_port'] ) ) : '587';
1209
1210 $wlgf_user_message = isset( $_POST['user_message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['user_message'] ) ) : '';
1211 $wlgf_msg_allowed_tags = array(
1212 'p' => array(),
1213 'strong' => array(),
1214 'br' => array(),
1215 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ),
1216 'hr' => array(),
1217 'i' => array(),
1218 'em' => array()
1219 );
1220 $wlgf_user_message = wp_kses( $wlgf_user_message, $wlgf_msg_allowed_tags );
1221
1222 // Newsletter Settings (PRO-008)
1223 $wlgf_newsletter_provider = isset( $_POST['newsletter_provider'] ) ? sanitize_text_field( wp_unslash( $_POST['newsletter_provider'] ) ) : 'local';
1224 if ( ! in_array( $wlgf_newsletter_provider, array( 'local', 'mailchimp' ), true ) ) {
1225 $wlgf_newsletter_provider = 'local';
1226 }
1227
1228 // Mailchimp API Key leave-blank-to-keep
1229 $wlgf_mailchimp_api_key = isset( $_POST['mailchimp_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_api_key'] ) ) : '';
1230 if ( empty( $wlgf_mailchimp_api_key ) && ! empty( $existing_settings['mailchimp_api_key'] ) ) {
1231 $wlgf_mailchimp_api_key = $existing_settings['mailchimp_api_key'];
1232 }
1233 $wlgf_mailchimp_list_id = isset( $_POST['mailchimp_list_id'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_list_id'] ) ) : '';
1234 $wlgf_mailchimp_optin = isset( $_POST['mailchimp_double_optin'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_double_optin'] ) ) : '0';
1235
1236 $wlgf_settings = array(
1237 'recaptcha' => $wlgf_recaptcha,
1238 'sitekey' => $wlgf_sitekey,
1239 'secretkey' => $wlgf_secretkey,
1240 'notify_admin' => $wlgf_notify_admin,
1241 'email_engine' => $wlgf_email_engine,
1242 'smtp_host' => $wlgf_smtp_host,
1243 'smtp_username' => $wlgf_smtp_username,
1244 'smtp_password' => $wlgf_smtp_password,
1245 'smtp_encryption' => $wlgf_smtp_encryption,
1246 'smtp_port' => $wlgf_smtp_port,
1247 'user_message' => $wlgf_user_message,
1248 'newsletter_provider' => $wlgf_newsletter_provider,
1249 'mailchimp_api_key' => $wlgf_mailchimp_api_key,
1250 'mailchimp_list_id' => $wlgf_mailchimp_list_id,
1251 'mailchimp_double_optin' => $wlgf_mailchimp_optin,
1252 );
1253
1254 // Preserve any existing settings not managed by Free (e.g. if site previously ran Pro)
1255 foreach ( $existing_settings as $key => $val ) {
1256 if ( ! isset( $wlgf_settings[ $key ] ) ) {
1257 $wlgf_settings[ $key ] = $val;
1258 }
1259 }
1260
1261 update_option( 'wlgf_settings', $wlgf_settings );
1262 wp_send_json_success( array( 'message' => __( 'Settings updated successfully.', 'lead-generation-form' ) ) );
1263 }
1264 wp_die();
1265 }
1266 }
1267 add_action( 'wp_ajax_wlgf_save_settings', 'wlgf_save_settings_callback' );
1268
1269 // 5.1 verify newsletter connection API callback (PRO-008)
1270 function wlgf_verify_newsletter_api_callback() {
1271 if ( ! current_user_can( 'manage_options' ) ) {
1272 wp_send_json_error( array( 'message' => __( 'Unauthorized user action.', 'lead-generation-form' ) ) );
1273 }
1274 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-save-settings' ) ) {
1275 wp_send_json_error( array( 'message' => __( 'Security verification failed.', 'lead-generation-form' ) ) );
1276 }
1277
1278 $provider = isset( $_POST['provider'] ) ? sanitize_text_field( wp_unslash( $_POST['provider'] ) ) : '';
1279 $api_key = isset( $_POST['api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['api_key'] ) ) : '';
1280 $list_id = isset( $_POST['list_id'] ) ? sanitize_text_field( wp_unslash( $_POST['list_id'] ) ) : '';
1281
1282 // Free version check: only Mailchimp is verified in Free edition
1283 if ( 'mailchimp' !== $provider ) {
1284 wp_send_json_error( array( 'message' => sprintf( __( '%s integration requires Lead Generation Form Pro.', 'lead-generation-form' ), ucfirst( $provider ) ) ) );
1285 }
1286
1287 $saved_settings = get_option( 'wlgf_settings', array() );
1288
1289 if ( empty( $api_key ) && ! empty( $saved_settings['mailchimp_api_key'] ) ) {
1290 $api_key = $saved_settings['mailchimp_api_key'];
1291 }
1292 if ( empty( $list_id ) && ! empty( $saved_settings['mailchimp_list_id'] ) ) {
1293 $list_id = $saved_settings['mailchimp_list_id'];
1294 }
1295
1296 require_once plugin_dir_path( __FILE__ ) . 'includes/class-wlgf-newsletter.php';
1297 $result = WLGF_Newsletter::verify_connection( 'mailchimp', array(
1298 'api_key' => $api_key,
1299 'list_id' => $list_id,
1300 ) );
1301
1302 if ( ! empty( $result['success'] ) ) {
1303 wp_send_json_success( array( 'message' => $result['message'] ) );
1304 } else {
1305 wp_send_json_error( array( 'message' => isset( $result['message'] ) ? $result['message'] : __( 'Verification failed.', 'lead-generation-form' ) ) );
1306 }
1307 }
1308 add_action( 'wp_ajax_wlgf_verify_newsletter_api', 'wlgf_verify_newsletter_api_callback' );
1309 // 5. save settings end
1310
1311 // 5.1 SMTP Test Email Handler (A1-02)
1312 function wlgf_smtp_test_mail_callback() {
1313 if ( ! current_user_can( 'manage_options' ) ) {
1314 wp_send_json_error( array( 'message' => __( 'Unauthorized access.', 'lead-generation-form' ) ) );
1315 }
1316 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wlgf-save-settings' ) ) {
1317 wp_send_json_error( array( 'message' => __( 'Security verification failed.', 'lead-generation-form' ) ) );
1318 }
1319
1320 $test_email = isset( $_POST['test_email'] ) ? sanitize_email( wp_unslash( $_POST['test_email'] ) ) : '';
1321 if ( ! is_email( $test_email ) ) {
1322 $test_email = get_option( 'admin_email' );
1323 }
1324
1325 $subject = sprintf( __( '[%s] Lead Generation Form - SMTP Test Email', 'lead-generation-form' ), get_bloginfo( 'name' ) );
1326 $message = __( "Congratulations! Your Lead Generation Form SMTP settings are functioning properly.\n\nDispatched at: ", 'lead-generation-form' ) . current_time( 'mysql' );
1327
1328 $sent = wp_mail( $test_email, $subject, $message );
1329 if ( $sent ) {
1330 wp_send_json_success( array( 'message' => sprintf( __( 'Test email successfully sent to %s!', 'lead-generation-form' ), esc_html( $test_email ) ) ) );
1331 } else {
1332 global $phpmailer;
1333 $error_msg = ( isset( $phpmailer->ErrorInfo ) && ! empty( $phpmailer->ErrorInfo ) ) ? $phpmailer->ErrorInfo : __( 'Mail dispatch failed. Please verify SMTP host, port, username, and app password.', 'lead-generation-form' );
1334 wp_send_json_error( array( 'message' => sprintf( __( 'Failed to dispatch test email: %s', 'lead-generation-form' ), esc_html( $error_msg ) ) ) );
1335 }
1336 }
1337 add_action( 'wp_ajax_wlgf_smtp_test_mail', 'wlgf_smtp_test_mail_callback' );
1338
1339 // 6. smtp setup start - Encryption Mapping Fix (A1-01)
1340 function wlgf_configure_smtp( $wlgf_phpmailer ){
1341 $wlgf_settings = get_option( 'wlgf_settings', array() );
1342 $wlgf_email_engine = isset( $wlgf_settings['email_engine'] ) ? sanitize_text_field( $wlgf_settings['email_engine'] ) : '';
1343
1344 if ( '2' === (string) $wlgf_email_engine ) {
1345 $wlgf_smtp_host = isset( $wlgf_settings['smtp_host'] ) ? sanitize_text_field( $wlgf_settings['smtp_host'] ) : '';
1346 $wlgf_smtp_username = isset( $wlgf_settings['smtp_username'] ) ? sanitize_text_field( $wlgf_settings['smtp_username'] ) : '';
1347 $wlgf_smtp_password = isset( $wlgf_settings['smtp_password'] ) ? sanitize_text_field( $wlgf_settings['smtp_password'] ) : '';
1348 $wlgf_smtp_encryption = isset( $wlgf_settings['smtp_encryption'] ) ? sanitize_text_field( $wlgf_settings['smtp_encryption'] ) : '1';
1349 $wlgf_smtp_port = isset( $wlgf_settings['smtp_port'] ) ? sanitize_text_field( $wlgf_settings['smtp_port'] ) : '587';
1350
1351 $wlgf_phpmailer->isSMTP();
1352 $wlgf_phpmailer->Host = $wlgf_smtp_host;
1353 $wlgf_phpmailer->SMTPAuth = true;
1354 $wlgf_phpmailer->Username = $wlgf_smtp_username;
1355 $wlgf_phpmailer->Password = $wlgf_smtp_password;
1356 $wlgf_phpmailer->Port = intval( $wlgf_smtp_port );
1357
1358 // Accurate encryption mapping fix: '1' => 'tls', '2' => 'ssl'
1359 if ( '1' === (string) $wlgf_smtp_encryption ) {
1360 $wlgf_phpmailer->SMTPSecure = 'tls';
1361 } elseif ( '2' === (string) $wlgf_smtp_encryption ) {
1362 $wlgf_phpmailer->SMTPSecure = 'ssl';
1363 } else {
1364 $wlgf_phpmailer->SMTPSecure = '';
1365 }
1366 }
1367 }
1368 // 6. smtp setup end
1369
1370 // 7. Gutenberg Block Registration
1371 require_once plugin_dir_path( __FILE__ ) . 'includes/blocks/lead-form-block.php';
1372
1373 // 8. Elementor Widget Registration
1374 require_once plugin_dir_path( __FILE__ ) . 'includes/elementor/class-wlgf-elementor-widget.php';
1375
1376 // 9. Visual Analytics & Drop-off Funnel Tracking (PRO-017)
1377 function wlgf_track_analytics_callback() {
1378 $form_id = isset( $_POST['form_id'] ) ? absint( $_POST['form_id'] ) : 0;
1379 $step = isset( $_POST['step'] ) ? max( 1, absint( $_POST['step'] ) ) : 1;
1380 $device = isset( $_POST['device'] ) ? sanitize_text_field( wp_unslash( $_POST['device'] ) ) : 'desktop';
1381
1382 if ( empty( $form_id ) ) {
1383 wp_send_json_error( array( 'message' => 'Missing form ID' ) );
1384 }
1385
1386 // Validate form existence to prevent arbitrary unauthenticated option injection
1387 $form_info = get_option( 'wlgf_form_' . $form_id );
1388 if ( empty( $form_info ) || ! is_array( $form_info ) ) {
1389 wp_send_json_error( array( 'message' => 'Form does not exist' ) );
1390 }
1391
1392 // Transient-based Rate Limiting (prevent automated bot inflation)
1393 $remote_ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '127.0.0.1';
1394 $rate_key = 'wlgf_tr_lmt_' . md5( $remote_ip . '_' . $form_id . '_' . $step );
1395 if ( false !== get_transient( $rate_key ) ) {
1396 wp_send_json_success( array( 'tracked' => false, 'rate_limited' => true ) );
1397 }
1398 set_transient( $rate_key, 1, 30 ); // 30-second cooldown per client/form/step
1399
1400 // Update legacy view counter
1401 $views_key = 'wlgf_form_views_' . $form_id;
1402 $views = intval( get_option( $views_key, 0 ) );
1403 update_option( $views_key, $views + 1, false );
1404
1405 // Update rich analytics record
1406 $analytics_key = 'wlgf_analytics_' . $form_id;
1407 $analytics = get_option( $analytics_key, array(
1408 'total_views' => 0,
1409 'total_submissions' => 0,
1410 'daily' => array(),
1411 'step_views' => array(),
1412 'device_breakdown' => array(
1413 'desktop' => 0,
1414 'mobile' => 0,
1415 'tablet' => 0,
1416 ),
1417 ) );
1418
1419 $analytics['total_views'] = isset( $analytics['total_views'] ) ? ( $analytics['total_views'] + 1 ) : 1;
1420
1421 $today = gmdate( 'Y-m-d' );
1422 if ( ! isset( $analytics['daily'][ $today ] ) ) {
1423 $analytics['daily'][ $today ] = array( 'views' => 0, 'submissions' => 0 );
1424 }
1425 $analytics['daily'][ $today ]['views']++;
1426
1427 // Track Step Views for Funnel Analysis
1428 if ( ! isset( $analytics['step_views'][ $step ] ) ) {
1429 $analytics['step_views'][ $step ] = 0;
1430 }
1431 $analytics['step_views'][ $step ]++;
1432
1433 // Track Device Breakdown
1434 if ( ! in_array( $device, array( 'desktop', 'mobile', 'tablet' ), true ) ) {
1435 $device = 'desktop';
1436 }
1437 if ( ! isset( $analytics['device_breakdown'][ $device ] ) ) {
1438 $analytics['device_breakdown'][ $device ] = 0;
1439 }
1440 $analytics['device_breakdown'][ $device ]++;
1441
1442 // Keep daily history capped at 90 days to prevent option bloat
1443 if ( count( $analytics['daily'] ) > 90 ) {
1444 ksort( $analytics['daily'] );
1445 $analytics['daily'] = array_slice( $analytics['daily'], -90, 90, true );
1446 }
1447
1448 update_option( $analytics_key, $analytics, false );
1449 wp_send_json_success( array( 'tracked' => true ) );
1450 }
1451 add_action( 'wp_ajax_wlgf_track_analytics', 'wlgf_track_analytics_callback' );
1452 add_action( 'wp_ajax_nopriv_wlgf_track_analytics', 'wlgf_track_analytics_callback' );
1453
1454