PluginProbe
GSheetConnector – WPForms Google Sheets Connector, Save WPForms Lite & PRO Entries to Database & Sheets / trunk
GSheetConnector – WPForms Google Sheets Connector, Save WPForms Lite & PRO Entries to Database & Sheets vtrunk
4.2.1 4.1.0 3.3 3.4 3.4.1 3.4.12 3.4.13 3.4.15 3.4.16 3.4.17 3.4.18 3.4.19 3.4.2 3.4.20 3.4.21 3.4.22 3.4.23 3.4.25 3.4.26 3.4.27 3.4.4 3.4.5 3.4.6 3.4.7 3.4.9 All 31 releases
gsheetconnector-wpforms / gsheetconnector-wpforms.php

gsheetconnector-wpforms.php in GSheetConnector – WPForms Google Sheets Connector, Save WPForms Lite & PRO Entries to Database & Sheets trunk, at gsheetconnector-wpforms.php

934 lines 30.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Plugin Name: GSheetConnector For WPForms
5 * Plugin URI: https://www.gsheetconnector.com/wpforms-google-sheet-connector-pro
6 * Description: Send your WPForms data to your Google Sheets spreadsheet.
7 * Requires at least: 5.6
8 * Requires PHP: 7.4
9 * Author: GSheetConnector
10 * Author URI: https://www.gsheetconnector.com/
11 * Version: 4.2.1
12 * Text Domain: gsheetconnector-wpforms
13 * License: GPLv2
14 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
15 * Domain Path: /languages
16 */
17
18 /* Exit if accessed directly. */
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit;
21 }
22 // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
23
24 // ============================================================
25 // SECTION 1: CONSTANTS & BOOTSTRAP
26 // ============================================================
27
28 define( 'WPFORMS_GOOGLESHEET_VERSION', '4.2.1' );
29 define( 'WPFORMS_GOOGLESHEET_DB_VERSION', '4.2.1' );
30 define( 'WPFORMS_GOOGLESHEET_ROOT', dirname( __FILE__ ) );
31 define( 'WPFORMS_GOOGLESHEET_URL', plugins_url( '/', __FILE__ ) );
32 define( 'WPFORMS_GOOGLESHEET_BASE_FILE', basename( dirname( __FILE__ ) ) . '/gsheetconnector-wpforms.php' );
33 define( 'WPFORMS_GOOGLESHEET_BASE_NAME', plugin_basename( __FILE__ ) );
34 define( 'WPFORMS_GOOGLESHEET_PATH', plugin_dir_path( __FILE__ ) );
35 define( 'WPFORMS_GOOGLESHEET_PRODUCT_NAME', 'Wpforms Google Sheet Connector' );
36 define( 'WPFORMS_GOOGLESHEET_API_URL', 'https://oauth.gsheetconnector.com/api-cred.php' );
37 define( 'WPFORMS_GOOGLESHEET_CURRENT_THEME', get_stylesheet_directory() );
38
39 /* Suppress "doing it wrong" notices from WP core during plugin init. */
40 add_filter( 'doing_it_wrong_trigger_error', '__return_false' );
41
42 /** add languages */
43 // phpcs:ignore PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound
44 load_plugin_textdomain(
45 'gsheetconnector-wpforms',
46 false,
47 basename( dirname( __FILE__ ) ) . '/languages'
48 );
49
50 // ============================================================
51 // SECTION 2: WPFORMS DEPENDENCY CHECK
52 // Sets $activate_the_plugin = true if WPForms is available
53 // in any supported install scenario.
54 // ============================================================
55
56 global $gsheetconnector_wpforms_activate_the_plugin;
57 $gsheetconnector_wpforms_activate_the_plugin = false;
58
59 /* Slugs for all supported WPForms editions. */
60 $gsheetconnector_wpforms_lite_check = 'wpforms-lite/wpforms.php';
61 $gsheetconnector_wpforms_free_check = 'wpforms/wpforms.php';
62 $gsheetconnector_wpforms_pro_check = 'wpforms-pro/wpforms.php';
63
64 $gsheetconnector_wpforms_current_site_id = get_current_blog_id();
65
66 if ( is_multisite() ) {
67
68 /* Case 1 – Per-site activation on a multisite network. */
69 if ( ! empty( $gsheetconnector_wpforms_current_site_id ) ) {
70 $gsheetconnector_wpforms_active_plugins = gscwpff_get_activated_plugins_for_site( $gsheetconnector_wpforms_current_site_id );
71
72 if (
73 in_array( $gsheetconnector_wpforms_lite_check, $gsheetconnector_wpforms_active_plugins ) ||
74 in_array( $gsheetconnector_wpforms_free_check, $gsheetconnector_wpforms_active_plugins ) ||
75 in_array( $gsheetconnector_wpforms_pro_check, $gsheetconnector_wpforms_active_plugins )
76 ) {
77 $gsheetconnector_wpforms_activate_the_plugin = true;
78 }
79 }
80
81 /* Case 2 – Network-wide (sitewide) activation. */
82 $gsheetconnector_wpforms_network_plugins = get_site_option( 'active_sitewide_plugins', [] );
83
84 if (
85 array_key_exists( $gsheetconnector_wpforms_lite_check, $gsheetconnector_wpforms_network_plugins ) ||
86 array_key_exists( $gsheetconnector_wpforms_free_check, $gsheetconnector_wpforms_network_plugins ) ||
87 array_key_exists( $gsheetconnector_wpforms_pro_check, $gsheetconnector_wpforms_network_plugins )
88 ) {
89 $gsheetconnector_wpforms_activate_the_plugin = true;
90 }
91 } else {
92
93 /* Case 3 – Standard single-site install. */
94 $gsheetconnector_wpforms_active_plugins = get_option( 'active_plugins', [] );
95
96 if (
97 in_array( $gsheetconnector_wpforms_lite_check, $gsheetconnector_wpforms_active_plugins ) ||
98 in_array( $gsheetconnector_wpforms_free_check, $gsheetconnector_wpforms_active_plugins ) ||
99 in_array( $gsheetconnector_wpforms_pro_check, $gsheetconnector_wpforms_active_plugins )
100 ) {
101 $gsheetconnector_wpforms_activate_the_plugin = true;
102 }
103 }
104
105 // ============================================================
106 // SECTION 3: HELPER FUNCTIONS
107 // ============================================================
108
109 /**
110 * Returns the list of active plugins for a specific site on a
111 * multisite network.
112 *
113 * @param int $site_id Blog / site ID.
114 * @return array Array of active plugin slugs.
115 */
116 if ( ! function_exists( 'gscwpff_get_activated_plugins_for_site' ) ) {
117 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
118 function gscwpff_get_activated_plugins_for_site( $site_id ) {
119 switch_to_blog( $site_id );
120 $activated_plugins = get_option( 'active_plugins', [] );
121 restore_current_blog();
122 return $activated_plugins;
123 }
124 }
125
126 // ============================================================
127 // SECTION 4: FREEMIUS SDK INITIALISATION
128 // Only loaded when WPForms is confirmed active.
129 // ============================================================
130
131 if ( $gsheetconnector_wpforms_activate_the_plugin && ! function_exists( 'gw_fs' ) ) {
132
133 /**
134 * Returns (and lazily initialises) the Freemius SDK instance.
135 *
136 * @return Freemius
137 */
138 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
139 function gw_fs() {
140 global $gw_fs;
141
142 if ( ! isset( $gw_fs ) ) {
143
144 /* Enable multisite network integration. */
145 if ( ! defined( 'WP_FS__PRODUCT_17697_MULTISITE' ) ) {
146 define( 'WP_FS__PRODUCT_17697_MULTISITE', true );
147 }
148
149 /* Load the Freemius SDK. */
150 require_once dirname( __FILE__ ) . '/lib/vendor/freemius/start.php';
151
152 $gw_fs = fs_dynamic_init( array(
153 'id' => '17697',
154 'slug' => 'gsheetconnector-wpforms',
155 'type' => 'plugin',
156 'public_key' => 'pk_6ebee3f42b58df138fbf6914afb87',
157 'is_premium' => false,
158 'has_addons' => false,
159 'has_paid_plans' => false,
160 'menu' => array(
161 'slug' => 'wpform-google-sheet-config',
162 'first-path' => 'admin.php?page=wpform-google-sheet-config',
163 'account' => false,
164 'contact' => false,
165 'support' => false,
166 ),
167 ) );
168 }
169
170 return $gw_fs;
171 }
172
173 gw_fs();
174 // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
175 do_action( 'gw_fs_loaded' );
176 }
177
178 // ============================================================
179 // SECTION 5: EARLY INCLUDES
180 // Classes that must be available before the main class boots.
181 // ============================================================
182
183 if ( ! class_exists( 'Wpform_gs_Connector_Utility' ) ) {
184 include WPFORMS_GOOGLESHEET_ROOT . '/includes/class-wpform-utility.php';
185 }
186
187 if ( ! class_exists( 'Wpform_gs_Connector_role_setting' ) ) {
188 include WPFORMS_GOOGLESHEET_ROOT . '/includes/pages/gs-wpform-role-setting.php';
189 }
190
191 // ============================================================
192 // SECTION 6: WPFORMS-LOADED CALLBACK
193 // Heavy integration files are deferred until WPForms itself is
194 // fully initialised, so all WPForms APIs are available.
195 // ============================================================
196
197 /**
198 * Loads Google Sheets integration classes after WPForms is ready.
199 *
200 * Hooked to: wpforms_loaded
201 */
202 function wpforms_Googlesheet_integration() {
203 require_once plugin_dir_path( __FILE__ ) . 'includes/class-wpforms-integration.php';
204 include_once WPFORMS_GOOGLESHEET_ROOT . '/lib/google-sheets.php';
205 require_once plugin_dir_path( __FILE__ ) . 'includes/wpforms-panel.php';
206 require_once plugin_dir_path( __FILE__ ) . 'includes/class-wpformdb.php';
207 }
208 add_action( 'wpforms_loaded', 'wpforms_Googlesheet_integration' );
209
210 // ============================================================
211 // SECTION 7: MAIN PLUGIN CLASS
212 // ============================================================
213
214 class WPforms_Gsheet_Connector_Init {
215
216 // --------------------------------------------------------
217 // 7.1 CONSTRUCTOR – register all hooks
218 // --------------------------------------------------------
219
220 public function __construct() {
221
222 /* Lifecycle hooks. */
223 register_activation_hook( __FILE__, array( $this, 'wpform_gs_connector_activate' ) );
224 register_deactivation_hook( __FILE__, array( $this, 'wpform_gs_connector_deactivate' ) );
225 register_uninstall_hook( __FILE__, array( 'WPforms_Gsheet_Connector_Init', 'wpform_gs_connector_uninstall' ) );
226
227 /* Admin init tasks. */
228 add_action( 'admin_init', array( $this, 'validate_parent_plugin_exists' ) );
229 add_action( 'admin_init', array( $this, 'run_on_upgrade' ) );
230 add_action( 'admin_init', array( $this, 'redirect_after_upgrade' ), 999 );
231
232 /* Admin UI. */
233 add_action( 'admin_menu', array( $this, 'register_wpform_menu_pages' ) );
234 add_action( 'wp_dashboard_setup', array( $this, 'add_wpform_gs_connector_summary_widget' ) );
235
236 /* Asset loading. */
237 add_action( 'init', array( $this, 'load_css_and_js_files' ) );
238 add_action( 'init', array( $this, 'load_all_classes' ) );
239
240 /* AJAX handlers. */
241 add_action( 'wp_ajax_wp_clear_logs', array( $this, 'wp_clear_logs' ) );
242 add_action( 'wp_ajax_wp_clear_debug_logs', array( $this, 'wp_clear_debug_logs' ) );
243
244 /* Plugin list customisation. */
245 add_filter( 'plugin_action_links_' . WPFORMS_GOOGLESHEET_BASE_NAME, array( $this, 'wpform_gs_connector_plugin_action_links' ) );
246 add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
247
248 /** Redirect URL */
249 add_action( 'admin_init', array( $this, 'handle_activation_redirect' ) );
250
251
252
253 }
254
255 // --------------------------------------------------------
256 // 7.2 ACTIVATION / DEACTIVATION / UNINSTALL
257 // --------------------------------------------------------
258
259 /**
260 * Runs on plugin activation.
261 * Handles both network-wide and per-site activation on multisite.
262 *
263 * @param bool $network_wide True when network-activated.
264 */
265 public function wpform_gs_connector_activate( $network_wide ) {
266
267 $this->run_on_activation();
268
269 if ( function_exists( 'is_multisite' ) && is_multisite() && $network_wide ) {
270 foreach ( get_sites( array( 'fields' => 'ids' ) ) as $blog_id ) {
271 switch_to_blog( $blog_id );
272 $this->run_for_site();
273 restore_current_blog();
274 }
275 return;
276 }
277
278 $this->run_for_site();
279
280 /** Activated plugin and redirect to dashboad */
281 set_transient( 'wpform_gs_activation_redirect', true, 30 );
282 }
283
284 /**
285 * Runs on plugin deactivation.
286 * Intentionally left empty – no cleanup required on deactivation.
287 */
288 public function wpform_gs_connector_deactivate() {}
289
290 /**
291 * Runs on plugin uninstall (static – called without an object instance).
292 * Removes all plugin data from every site on a multisite network,
293 * or from the single site on a standard install.
294 */
295 public static function wpform_gs_connector_uninstall() {
296
297 self::run_on_uninstall();
298
299 if ( is_multisite() ) {
300 foreach ( get_sites( array( 'fields' => 'ids' ) ) as $site_id ) {
301 switch_to_blog( $site_id );
302 self::delete_for_site();
303 restore_current_blog();
304 }
305 return;
306 }
307
308 self::delete_for_site();
309 }
310
311 // --------------------------------------------------------
312 // 7.3 DATABASE SETUP & UPGRADES
313 // --------------------------------------------------------
314
315 /**
316 * Creates (or upgrades) the custom submission table and the error-log
317 * table using dbDelta so it is safe to call repeatedly.
318 */
319 public function gsc_wpforms_create_table() {
320
321 global $wpdb;
322 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
323
324 $charset_collate = $wpdb->get_charset_collate();
325
326 /* Submissions table. */
327 $submissions_table = $wpdb->prefix . 'gscwpforms_submissions';
328 dbDelta( "CREATE TABLE {$submissions_table} (
329 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
330 entry_id BIGINT(20) UNSIGNED NOT NULL,
331 form_id BIGINT(20) UNSIGNED NOT NULL,
332 submitted_at DATETIME NOT NULL,
333 form_data LONGTEXT NOT NULL,
334 user_email VARCHAR(200) DEFAULT NULL,
335 user_ip VARCHAR(100) DEFAULT NULL,
336 browser_info VARCHAR(255) DEFAULT NULL,
337 is_read TINYINT(1) NOT NULL DEFAULT 1,
338 PRIMARY KEY (id),
339 KEY form_id (form_id),
340 KEY entry_id (entry_id)
341 ) {$charset_collate};" );
342
343 /* Error-log table. */
344 $log_table = $wpdb->prefix . 'gscwpf_error_logs';
345 dbDelta( "CREATE TABLE {$log_table} (
346 id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
347 error_id VARCHAR(191) NOT NULL,
348 code INT NOT NULL,
349 message TEXT NOT NULL,
350 details LONGTEXT NULL,
351 created_at DATETIME NOT NULL,
352 PRIMARY KEY (id),
353 KEY error_id (error_id),
354 KEY code (code)
355 ) {$charset_collate};" );
356 }
357
358 /**
359 * Runs version-specific upgrade routines on every admin page load.
360 * Updates the stored version number after all upgrades complete.
361 */
362 public function run_on_upgrade() {
363
364 $plugin_options = get_site_option( 'wpform_GS_info' );
365
366 if ( $plugin_options['version'] <= '1.3' ) {
367 $this->upgrade_database_20();
368 $this->upgrade_database_21();
369 } elseif ( $plugin_options['version'] === '3.4.25' ) {
370 $this->upgrade_database_21();
371 } elseif ( $plugin_options['version'] === '4.0.4' ) {
372 $this->upgrade_database_22();
373 }
374
375 update_site_option( 'wpform_GS_info', array(
376 'version' => WPFORMS_GOOGLESHEET_VERSION,
377 'db_version' => WPFORMS_GOOGLESHEET_DB_VERSION,
378 ) );
379
380 /** update existing method default when the plugin upgrade */
381 if (get_option('gs_wpforms_manual_setting') === false) {
382 update_option('gs_wpforms_manual_setting', 0);
383 }
384
385 /** Oauth Option value migrate function
386 * wpforms_existing = 0
387 * wpforms_service = 2
388 */
389
390 /*if ( method_exists( $this, 'oauth_option_migrate_function' ) ) {
391 $this->oauth_option_migrate_function();
392 }*/
393
394 }
395
396 /**
397 * Upgrade routine for schema version 2.0.
398 * Sets a transient so the admin is redirected to the integration page.
399 */
400 public function upgrade_database_20() {
401 $this->run_multisite_or_single( 'upgrade_helper_20' );
402 }
403
404 /** @internal Called per-site by upgrade_database_20(). */
405 public function upgrade_helper_20() {
406 set_transient( 'wpform_gs_upgrade_redirect', true, 30 );
407 }
408
409 /**
410 * Upgrade routine for schema version 2.1.
411 * Re-fetches and saves Google API credentials.
412 */
413 public function upgrade_database_21() {
414 $this->run_multisite_or_single( 'upgrade_helper_21' );
415 }
416
417 /** @internal Called per-site by upgrade_database_21(). */
418 public function upgrade_helper_21() {
419 Wpform_gs_Connector_Utility::instance()->save_api_credentials();
420 }
421
422 /**
423 * Upgrade routine for schema version 2.2.
424 * Creates / updates the custom database tables.
425 */
426 public function upgrade_database_22() {
427 $this->run_multisite_or_single( 'upgrade_helper_22' );
428 }
429
430 /** @internal Called per-site by upgrade_database_22(). */
431 public function upgrade_helper_22() {
432 $this->gsc_wpforms_create_table();
433 }
434
435 /*public function oauth_option_migrate_function(){
436 $option_name = 'gs_wpforms_manual_setting';
437 $current_value = get_option($option_name);
438
439 // If option doesn't exist, set default value 0
440 if ( false === $current_value ) {
441 update_option( $option_name, 0 );
442 return;
443 }
444
445 // Skip if already migrated (numeric value)
446 if ( is_numeric( $current_value ) ) {
447 return;
448 }
449
450 $migration_map = [
451 'wpforms_existing' => 0,
452 'wpforms_service' => 2,
453 ];
454
455 if ( isset( $migration_map[ $current_value ] ) ) {
456 update_option( $option_name, $migration_map[ $current_value ] );
457 }
458 }*/
459
460
461 /**
462 * Redirects the admin to the integration settings page after a 2.0 upgrade.
463 * Fires on admin_init (priority 999).
464 */
465 public function redirect_after_upgrade() {
466
467 if ( ! get_transient( 'wpform_gs_upgrade_redirect' ) ) {
468 return;
469 }
470
471 $plugin_options = get_site_option( 'wpform_GS_info' );
472
473 if ( $plugin_options['version'] === '2.0' ) {
474 delete_transient( 'wpform_gs_upgrade_redirect' );
475 wp_safe_redirect( 'admin.php?page=wpform-google-sheet-config&tab=integration' );
476 }
477 }
478
479 // --------------------------------------------------------
480 // 7.4 ADMIN UI – MENUS, DASHBOARD WIDGET, NOTICES
481 // --------------------------------------------------------
482
483 /**
484 * Checks that at least one WPForms edition is active.
485 * Deactivates this plugin and shows an admin notice if not.
486 *
487 * Hooked to: admin_init
488 */
489 public function validate_parent_plugin_exists() {
490
491 $plugin = plugin_basename( __FILE__ );
492
493 $wpforms_active =
494 is_plugin_active( 'wpforms-lite/wpforms.php' ) ||
495 is_plugin_active( 'wpforms/wpforms.php' ) ||
496 is_plugin_active( 'wpforms-pro/wpforms.php' );
497
498 if ( ! $wpforms_active ) {
499 add_action( 'admin_notices', array( $this, 'wpforms_missing_notice' ) );
500 add_action( 'network_admin_notices', array( $this, 'wpforms_missing_notice' ) );
501 deactivate_plugins( $plugin );
502 unset( $_GET['activate'] );
503 }
504 }
505
506 /**
507 * Renders the admin notice shown when WPForms is missing.
508 */
509 public function wpforms_missing_notice() {
510 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin_notice() intentionally returns HTML markup; the message here is built entirely from static translated strings and a hardcoded link, not request input.
511 echo Wpform_gs_Connector_Utility::instance()->admin_notice( array(
512 'type' => 'error',
513 'message' => esc_html__( 'WPForms Google Sheet Connector Add-on requires WPForms ', 'gsheetconnector-wpforms' )
514 . esc_html__( ' plugin to be installed and activated.', 'gsheetconnector-wpforms' ),
515 ) );
516 }
517
518 /**
519 * Registers the "Google Sheet" submenu under the WPForms top-level menu.
520 *
521 * Hooked to: admin_menu
522 */
523 public function register_wpform_menu_pages() {
524
525 if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'activate_plugins' ) ) {
526 return;
527 }
528
529 if ( ! is_plugin_active( 'gsheetconnector-wpforms-pro/gsheetconnector-wpforms-pro.php' ) ) {
530
531 add_submenu_page(
532 'wpforms-overview',
533 __( 'Google Sheet', 'gsheetconnector-wpforms' ),
534 __( 'Google Sheet', 'gsheetconnector-wpforms' ),
535 'manage_options',
536 'wpform-google-sheet-config',
537 array( $this, 'wpforms_google_sheet_config' )
538 );
539 }
540 }
541
542 /**
543 * Renders the Google Sheets settings page.
544 */
545 public function wpforms_google_sheet_config() {
546 include WPFORMS_GOOGLESHEET_PATH . 'includes/pages/wpforms-gs-settings.php';
547 }
548
549 /**
550 * Registers the dashboard summary widget.
551 *
552 * Hooked to: wp_dashboard_setup
553 */
554 public function add_wpform_gs_connector_summary_widget() {
555
556 $title = sprintf(
557 "<img style='width:30px;margin-right:10px;' src='%s'><span>%s</span>",
558 esc_url( WPFORMS_GOOGLESHEET_URL . 'assets/img/wpforms-gsc.svg' ),
559 __( 'GSheetConnector For WPForms', 'gsheetconnector-wpforms' )
560 );
561
562 wp_add_dashboard_widget(
563 'wpform_gs_dashboard',
564 $title,
565 array( $this, 'wpform_gs_connector_summary_dashboard' )
566 );
567 }
568
569 /**
570 * Renders the dashboard widget content.
571 */
572 public function wpform_gs_connector_summary_dashboard() {
573 include_once WPFORMS_GOOGLESHEET_ROOT . '/includes/pages/wpform-dashboard-widget.php';
574 }
575
576 // --------------------------------------------------------
577 // 7.5 PLUGIN LIST TABLE CUSTOMISATION
578 // --------------------------------------------------------
579
580 /**
581 * Adds Settings and Upgrade-to-PRO links to the plugin's action links.
582 *
583 * @param array $links Default action links.
584 * @return array Modified action links.
585 */
586 public function wpform_gs_connector_plugin_action_links( $links ) {
587
588 unset( $links['edit'] ); /* Remove the "Edit" link. */
589
590 if ( ! is_plugin_active( 'gsheetconnector-wpforms-pro/gsheetconnector-wpforms-pro.php' ) ) {
591 return array_merge(
592 array(
593 '<a href="' . admin_url( 'admin.php?page=wpform-google-sheet-config&tab=integration' ) . '">'
594 . __( 'Settings', 'gsheetconnector-wpforms' ) . '</a>',
595 '<a href="https://www.gsheetconnector.com/wpforms-google-sheet-connector-pro" target="_blank"> <span style="color: green;">'
596 . __( 'Upgrade to PRO', 'gsheetconnector-wpforms' ) . '</span></a>',
597 ),
598 $links
599 );
600 } else {
601 // PRO plugin is active - show Settings link only
602 return array_merge(
603 array(
604 '<a href="' . admin_url( 'admin.php?page=wpform-google-sheet-config&tab=integration' ) . '">'
605 . __( 'Settings', 'gsheetconnector-wpforms' ) . '</a>',
606 ),
607 $links
608 );
609 }
610 }
611
612 /**
613 * Appends Docs and Support links to the plugin's row meta.
614 *
615 * @param array $plugin_meta Array of existing meta links.
616 * @param string $plugin_file Plugin basename used for matching.
617 * @return array Modified meta links.
618 */
619 public function plugin_row_meta( $plugin_meta, $plugin_file ) {
620
621 if ( WPFORMS_GOOGLESHEET_BASE_NAME !== $plugin_file ) {
622 return $plugin_meta;
623 }
624
625 return array_merge( $plugin_meta, array(
626 'docs' => '<a href="https://www.gsheetconnector.com/docs/gsheetconnnector-for-wpforms/introduction" target="_blank" aria-label="'
627 . esc_attr( esc_html__( 'View Documentation', 'gsheetconnector-wpforms' ) ) . '">'
628 . esc_html__( 'Docs', 'gsheetconnector-wpforms' ) . '</a>',
629 'ideo' => '<a href="https://www.gsheetconnector.com/support" aria-label="'
630 . esc_attr( esc_html__( 'Get Support', 'gsheetconnector-wpforms' ) ) . '" target="_blank">'
631 . esc_html__( 'Support', 'gsheetconnector-wpforms' ) . '</a>',
632 ) );
633 }
634
635 // --------------------------------------------------------
636 // 7.6 ASSET LOADING
637 // --------------------------------------------------------
638
639 /**
640 * Registers admin_print_styles and admin_print_scripts hooks.
641 *
642 * Hooked to: init
643 */
644 public function load_css_and_js_files() {
645 add_action( 'admin_print_styles', array( $this, 'add_css_files' ) );
646 add_action( 'admin_print_scripts', array( $this, 'add_js_files' ) );
647 }
648
649 /**
650 * Enqueues plugin CSS – only on the plugin's own admin page.
651 */
652 public function add_css_files() {
653
654 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page-identity check used to decide whether to enqueue assets, no state change.
655 if ( ! is_admin() || ! isset( $_GET['page'] ) || $_GET['page'] !== 'wpform-google-sheet-config' ) {
656 return;
657 }
658
659 $css_files = array(
660 'gswpf-css-free' => 'assets/css/wpform-gs-connector.css',
661 'gswpf-connector-header-free' => 'assets/css/header.css',
662 'gswpf-connector-footer-free' => 'assets/css/footer.css',
663 'gswpf-connector-extra-style-free' => 'assets/css/extra-style.css',
664 'gswpf-connector-global-free' => 'assets/css/global.css',
665 'gswpf-connector-responsive-free' => 'assets/css/responsive.css',
666 'gswpf-connector-pro-feature' => 'assets/css/pro-feature.css',
667 'gswpf-connector-font-awesome-free'=> 'assets/css/fontawesome.css',
668 );
669
670 foreach ( $css_files as $handle => $path ) {
671 wp_enqueue_style( $handle, WPFORMS_GOOGLESHEET_URL . $path, [], WPFORMS_GOOGLESHEET_VERSION, 'all' );
672 }
673 }
674
675 /**
676 * Enqueues plugin JS.
677 * Page-specific scripts only load on the plugin's own admin page;
678 * notice scripts load on all admin pages.
679 */
680 public function add_js_files() {
681
682 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page-identity check used to decide whether to enqueue assets, no state change.
683 if ( is_admin() && isset( $_GET['page'] ) && $_GET['page'] === 'wpform-google-sheet-config' ) {
684
685 /* Main connector script with AJAX object. */
686 wp_enqueue_script(
687 'wpform-gsconnectorjs',
688 WPFORMS_GOOGLESHEET_URL . 'assets/js/wpform-gs-connector.js',
689 array( 'jquery' ),
690 WPFORMS_GOOGLESHEET_VERSION,
691 true
692 );
693 wp_localize_script( 'wpform-gsconnectorjs', 'gsc_ajax', array(
694 'ajax_url' => admin_url( 'admin-ajax.php' ),
695 ) );
696
697 /* System debug and extension scripts. */
698 $page_scripts = array(
699 'gscwpform-debug-js' => 'assets/js/system-debug.js',
700 'gs-connector-extensions' => 'assets/js/gs-connector-extensions.js',
701 );
702
703 foreach ( $page_scripts as $handle => $path ) {
704 wp_enqueue_script( $handle, WPFORMS_GOOGLESHEET_URL . $path, array( 'jquery' ), WPFORMS_GOOGLESHEET_VERSION, true );
705 }
706 }
707
708 /* Notice + add-on scripts: load on all admin pages. */
709 if ( is_admin() ) {
710 wp_enqueue_script( 'wpform-gs-connector-notice-js', WPFORMS_GOOGLESHEET_URL . 'assets/js/wpforms-gs-connector-notice.js', [], WPFORMS_GOOGLESHEET_VERSION, true );
711 wp_enqueue_script( 'wpform-gs-connector-adds-js', WPFORMS_GOOGLESHEET_URL . 'assets/js/wpform-gs-connector-adds.js', [], WPFORMS_GOOGLESHEET_VERSION, true );
712 }
713 }
714
715 // --------------------------------------------------------
716 // 7.7 LAZY CLASS LOADING
717 // --------------------------------------------------------
718
719 /**
720 * Loads optional feature classes that are not needed at boot time.
721 *
722 * Hooked to: init
723 */
724 public function load_all_classes() {
725
726 if ( ! class_exists( 'Wpform_gs_Connector_Adds' ) ) {
727 include WPFORMS_GOOGLESHEET_PATH . 'includes/class-wpform-adds.php';
728 }
729
730 if ( ! class_exists( 'gswpff_error_logs' ) ) {
731 include WPFORMS_GOOGLESHEET_PATH . 'includes/class-gsc-wpforms-error-logs.php';
732 }
733 }
734
735 // --------------------------------------------------------
736 // 7.8 AJAX HANDLERS – LOG CLEARING
737 // --------------------------------------------------------
738
739 /**
740 * Clears the plugin's own debug log file.
741 * Callback for: wp_ajax_wp_clear_logs
742 */
743 public function wp_clear_logs() {
744
745 check_ajax_referer( 'gs-ajax-nonce', 'security' );
746
747 $wp_filesystem = $this->get_wp_filesystem();
748 $existDebugFile = get_option( 'wpf_gs_debug_log_file' );
749 $clear_file_msg = '';
750
751 if ( ! empty( $existDebugFile ) && $wp_filesystem->exists( $existDebugFile ) ) {
752 $wp_filesystem->put_contents( $existDebugFile, '', FS_CHMOD_FILE );
753 $clear_file_msg = 'Logs are cleared.';
754 } else {
755 $clear_file_msg = 'No log file exists to clear logs.';
756 }
757
758 wp_send_json_success( $clear_file_msg );
759 }
760
761 /**
762 * Clears the WordPress core debug.log file (System Status tab).
763 * Callback for: wp_ajax_wp_clear_debug_logs
764 */
765 public function wp_clear_debug_logs() {
766
767 check_ajax_referer( 'gs-ajax-nonce', 'security' );
768
769 $wp_filesystem = $this->get_wp_filesystem();
770 $log_file = WP_CONTENT_DIR . '/debug.log';
771
772 $wp_filesystem->put_contents( $log_file, '', FS_CHMOD_FILE );
773
774 wp_send_json_success();
775 }
776
777 // --------------------------------------------------------
778 // 7.9 PRIVATE HELPERS
779 // --------------------------------------------------------
780
781 /**
782 * Initialises WP_Filesystem and returns the global instance.
783 *
784 * @return WP_Filesystem_Base
785 */
786 private function get_wp_filesystem() {
787
788 if ( ! function_exists( 'WP_Filesystem' ) ) {
789 require_once ABSPATH . 'wp-admin/includes/file.php';
790 }
791 global $wp_filesystem;
792 WP_Filesystem();
793 return $wp_filesystem;
794 }
795
796 /**
797 * Runs a helper method on every site in a multisite network, or once
798 * on a single-site install.
799 *
800 * @param string $method_name Name of the instance method to call.
801 */
802 private function run_multisite_or_single( $method_name ) {
803
804 if ( is_multisite() ) {
805 foreach ( get_sites( array( 'fields' => 'ids' ) ) as $site_id ) {
806 switch_to_blog( $site_id );
807 $this->$method_name();
808 restore_current_blog();
809 }
810 return;
811 }
812
813 $this->$method_name();
814 }
815
816 /**
817 * Called on activation. Creates site-level options and runs
818 * any pending upgrade routines.
819 */
820 private function run_on_activation() {
821
822 $plugin_options = get_site_option( 'wpform_GS_info' );
823
824 $this->gsc_wpforms_create_table();
825
826 if ( $plugin_options === false ) {
827 update_site_option( 'wpform_GS_info', array(
828 'version' => WPFORMS_GOOGLESHEET_VERSION,
829 'db_version' => WPFORMS_GOOGLESHEET_DB_VERSION,
830 ) );
831 } elseif ( WPFORMS_GOOGLESHEET_DB_VERSION !== $plugin_options['version'] ) {
832 $this->run_on_upgrade();
833 }
834
835 if (!get_option('gscwpff_plugin_activated_at')) {
836 update_option('gscwpff_plugin_activated_at', time());
837 }
838
839 Wpform_gs_Connector_Utility::instance()->save_api_credentials();
840
841
842 }
843
844
845 public function handle_activation_redirect() {
846 if ( get_transient( 'wpform_gs_activation_redirect' ) ) {
847 delete_transient( 'wpform_gs_activation_redirect' ); // Remove it immediately
848
849 wp_safe_redirect( add_query_arg(
850 array(
851 'page' => 'wpform-google-sheet-config',
852 'tab' => 'wpform-dashboard'
853 ),
854 admin_url( 'admin.php' )
855 ) );
856 exit;
857 }
858 }
859
860 /**
861 * Creates per-site WordPress options with safe defaults on activation.
862 */
863 private function run_for_site() {
864
865 $defaults = array(
866 'wpform_gs_access_code' => '',
867 'gs_wpforms_manual_setting' => '0',
868 'wpform_gs_verify' => '',
869 'wpform_gs_verify_service' => '',
870 'wpform_gs_token' => '',
871 'gs_wpformspro_service_account_json'=> '',
872 'wpform_uninstall' => 'false',
873 'wpform_gs_verify_manual' => '',
874 );
875
876 foreach ( $defaults as $option => $default_value ) {
877 if ( ! get_option( $option ) ) {
878 update_option( $option, $default_value );
879 }
880 }
881 }
882
883 /**
884 * Deletes all per-site options and post-meta on uninstall.
885 */
886 private static function delete_for_site() {
887
888 $get_option = get_option('gscwpff_wpforms_uninstall_settings_free');
889 if ($get_option === '1') {
890 $options = array(
891 'wpform_gs_access_code',
892 'wpform_gs_verify',
893 'wpform_gs_token',
894 'gs_wpforms_manual_setting',
895 'gs_wpformspro_service_account_json',
896 'wpform_uninstall',
897 'wpform_gs_verify_manual',
898 'wpform_gs_verify_service',
899 );
900
901 foreach ( $options as $option ) {
902 delete_option( $option );
903 }
904
905 delete_post_meta_by_key( 'wpform_gs_settings' );
906 delete_post_meta_by_key( 'wpform_gs_settings_new' );
907 }
908 }
909
910 /**
911 * Deletes the network-level (site) option on uninstall.
912 * Guards against direct file access.
913 */
914 private static function run_on_uninstall() {
915
916 if ( ! defined( 'ABSPATH' ) || ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
917 exit();
918 }
919
920 delete_site_option( 'wpform_GS_info' );
921 }
922
923
924 }
925
926 // ============================================================
927 // SECTION 8: BOOT
928 // ============================================================
929
930
931
932
933
934 $gsheetconnector_wpforms_init = new WPforms_Gsheet_Connector_Init();