PluginProbe
SureContact – Newsletters, Email Marketing, Automation, Revenue Tracking & CRM / trunk
SureContact – Newsletters, Email Marketing, Automation, Revenue Tracking & CRM vtrunk
1.5.4 1.5.3 1.5.2 1.5.1 trunk 0.0.2 0.0.3 0.0.4 0.0.5 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.5.0
surecontact / surecontact.php

surecontact.php in SureContact – Newsletters, Email Marketing, Automation, Revenue Tracking & CRM trunk, at surecontact.php

556 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: SureContact – Newsletters, Email Marketing, Automation, Revenue Tracking & CRM
4 * Plugin URI: https://surecontact.com
5 * Description: Send newsletters, set up email automations, manage contacts and track ecommerce revenue in a CRM for WordPress.
6 * Version: 1.5.4
7 * Author: SureContact
8 * Author URI: https://profiles.wordpress.org/brainstormforce/
9 * License: GPLv2 or later
10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 * Text Domain: surecontact
12 * Domain Path: /languages
13 * Requires at least: 5.8
14 * Tested up to: 7.1
15 * Requires PHP: 7.4
16 *
17 * @since 0.0.1
18 *
19 * @package SureContact
20 */
21
22 // Exit if accessed directly.
23 if ( ! defined( 'ABSPATH' ) ) {
24 exit;
25 }
26
27 /**
28 * SaaS API Base URL constant
29 *
30 * @since 0.0.1
31 */
32 if ( ! defined( 'SURECONTACT_SAAS_API_BASE_URL' ) ) {
33 define( 'SURECONTACT_SAAS_API_BASE_URL', 'https://api.surecontact.com' );
34 }
35
36 /**
37 * SaaS SDK Base URL constant
38 *
39 * @since 0.0.1
40 */
41 if ( ! defined( 'SURECONTACT_SAAS_BASE_URL' ) ) {
42 define( 'SURECONTACT_SAAS_BASE_URL', 'https://app.surecontact.com' );
43 }
44
45 /**
46 * Main plugin class
47 *
48 * Singleton pattern implementation for SureContact plugin.
49 * Handles plugin initialization, dependency loading, and lifecycle hooks.
50 *
51 * @since 0.0.1
52 */
53 final class SureContact {
54
55 /**
56 * Plugin version
57 *
58 * @since 0.0.1
59 *
60 * @var string
61 */
62 const VERSION = '1.5.4';
63
64 /**
65 * Plugin singleton instance
66 *
67 * @since 0.0.1
68 *
69 * @var SureContact|null
70 */
71 private static $instance = null;
72
73 /**
74 * Plugin directory path
75 *
76 * @since 0.0.1
77 *
78 * @var string
79 */
80 private $plugin_path;
81
82 /**
83 * Plugin directory URL
84 *
85 * @since 0.0.1
86 *
87 * @var string
88 */
89 private $plugin_url;
90
91 /**
92 * Integrations loader instance
93 *
94 * @since 0.0.1
95 *
96 * @var SureContact\Integrations_Loader|null
97 */
98 public $integrations_loader;
99
100 /**
101 * Daily Sync Manager instance
102 *
103 * @since 0.0.1
104 *
105 * @var SureContact\Daily_Sync_Manager|null
106 */
107 private $daily_sync_manager;
108
109
110 /**
111 * Bulk Sync Service instance
112 *
113 * @since 0.0.1
114 *
115 * @var SureContact\Bulk_Sync_Service|null
116 */
117 private $bulk_sync_service;
118
119 /**
120 * Admin Menu instance
121 *
122 * @since 0.0.1
123 *
124 * @var SureContact\Admin\Admin_Menu|null
125 */
126 private $admin_menu;
127
128
129 /**
130 * Queue Manager instance
131 *
132 * @since 0.0.1
133 *
134 * @var SureContact\Queue_Manager|null
135 */
136 private $queue_manager;
137
138 /**
139 * Abandoned Cart Manager instance
140 *
141 * @since 1.5.0
142 *
143 * @var SureContact\Abandoned_Cart_Manager|null
144 */
145 private $abandoned_cart_manager;
146
147 /**
148 * Get singleton instance
149 *
150 * Implements lazy loading for better performance.
151 *
152 * @since 0.0.1
153 *
154 * @return SureContact
155 */
156 public static function get_instance() {
157 if ( is_null( self::$instance ) ) {
158 self::$instance = new self();
159 }
160 return self::$instance;
161 }
162
163 /**
164 * Private constructor to prevent direct instantiation
165 *
166 * @since 0.0.1
167 */
168 private function __construct() {
169 $this->define_constants();
170 $this->setup_hooks();
171 $this->load_dependencies();
172 $this->init_hooks();
173 }
174
175 /**
176 * Define plugin constants
177 *
178 * @since 0.0.1
179 *
180 * @return void
181 */
182 private function define_constants() {
183 $this->plugin_path = plugin_dir_path( __FILE__ );
184 $this->plugin_url = plugin_dir_url( __FILE__ );
185
186 define( 'SURECONTACT_VERSION', self::VERSION );
187 define( 'SURECONTACT_PLUGIN_PATH', $this->plugin_path );
188 define( 'SURECONTACT_PLUGIN_URL', $this->plugin_url );
189 define( 'SURECONTACT_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
190 }
191
192 /**
193 * Setup activation and deactivation hooks
194 *
195 * @since 0.0.1
196 *
197 * @return void
198 */
199 private function setup_hooks() {
200 register_activation_hook( __FILE__, array( $this, 'activate' ) );
201 register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
202 }
203
204 /**
205 * Load required dependencies
206 *
207 * @since 0.0.1
208 *
209 * @return void
210 */
211 private function load_dependencies() {
212 $this->load_action_scheduler();
213
214 // Load global helper functions.
215 require_once SURECONTACT_PLUGIN_PATH . 'includes/functions.php';
216
217 $autoloader_path = SURECONTACT_PLUGIN_PATH . 'includes/class-autoloader.php';
218 if ( file_exists( $autoloader_path ) ) {
219 require_once $autoloader_path;
220 } else {
221 // Autoloader not loaded yet, use direct error_log.
222 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
223 error_log(
224 '[' . gmdate( 'Y-m-d H:i:s' ) . '] SureContact [ERROR] Plugin: Critical error - autoloader not found at ' . $autoloader_path
225 );
226 }
227 }
228
229 /**
230 * Initialize plugin components via WordPress hooks
231 *
232 * @since 0.0.1
233 *
234 * @return void
235 */
236 private function init_hooks() {
237 // Ensure database tables exist (safety check for edge cases).
238 add_action( 'plugins_loaded', array( $this, 'maybe_create_tables' ), 1 );
239
240 add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
241 add_action( 'init', array( $this, 'init_queue_manager' ), 2 );
242 add_action( 'init', array( $this, 'init_abandoned_cart_manager' ), 2 );
243 add_action( 'init', array( $this, 'init_daily_sync_manager' ), 3 );
244 add_action( 'init', array( $this, 'init_bulk_sync_service' ), 5 );
245 // Load integrations early on init hook to ensure hooks are registered before plugin actions fire.
246 add_action( 'init', array( $this, 'init_integrations' ), 5 );
247 add_action( 'rest_api_init', array( $this, 'init_rest_api' ) );
248
249 SureContact\Abilities\Abilities_Loader::instance()->init();
250
251 if ( is_admin() ) {
252 add_action( 'admin_menu', array( $this, 'init_admin_menus' ) );
253 add_filter( 'plugin_action_links_' . SURECONTACT_PLUGIN_BASENAME, array( $this, 'add_action_links' ) );
254 add_filter( 'plugin_row_meta', array( $this, 'add_meta_links' ), 10, 2 );
255 }
256 }
257
258 /**
259 * Load plugin textdomain for translations
260 *
261 * @since 1.4.0
262 *
263 * @return void
264 */
265 public function load_textdomain() {
266 load_plugin_textdomain(
267 'surecontact',
268 false,
269 dirname( SURECONTACT_PLUGIN_BASENAME ) . '/languages'
270 );
271 }
272
273 /**
274 * Initialize Queue Manager.
275 *
276 * @since 0.0.1
277 *
278 * @return void
279 */
280 public function init_queue_manager() {
281 if ( is_null( $this->queue_manager ) ) {
282 $this->queue_manager = new SureContact\Queue_Manager();
283 }
284 }
285
286
287 /**
288 * Initialize Daily Sync Manager
289 *
290 * @since 0.0.1
291 *
292 * @return void
293 */
294 public function init_daily_sync_manager() {
295 if ( is_null( $this->daily_sync_manager ) ) {
296 $this->daily_sync_manager = new SureContact\Daily_Sync_Manager();
297 }
298 }
299
300
301 /**
302 * Initialize Abandoned Cart Manager
303 *
304 * @since 1.5.0
305 *
306 * @return void
307 */
308 public function init_abandoned_cart_manager() {
309 if ( is_null( $this->abandoned_cart_manager ) ) {
310 $this->abandoned_cart_manager = new SureContact\Abandoned_Cart_Manager();
311 }
312 }
313
314 /**
315 * Initialize admin menus
316 *
317 * @since 0.0.1
318 *
319 * @return void
320 */
321 public function init_admin_menus() {
322 if ( is_null( $this->admin_menu ) ) {
323 $this->admin_menu = new SureContact\Admin\Admin_Menu();
324 }
325 }
326
327 /**
328 * Initialize REST API
329 *
330 * @since 0.0.1
331 *
332 * @return void
333 */
334 public function init_rest_api() {
335 $rest_controller = new SureContact\API_WP\Rest_Controller();
336 $rest_controller->register_routes();
337
338 SureContact\API_WP\Api_Init::instance();
339 }
340
341 /**
342 * Initialize Bulk Sync Service
343 *
344 * @since 0.0.1
345 *
346 * @return void
347 */
348 public function init_bulk_sync_service() {
349 if ( is_null( $this->bulk_sync_service ) ) {
350 $this->bulk_sync_service = new SureContact\Bulk_Sync_Service();
351 }
352 }
353
354 /**
355 * Initialize integrations loader
356 *
357 * @since 0.0.1
358 *
359 * @return void
360 */
361 public function init_integrations() {
362 $this->integrations_loader = new SureContact\Integrations_Loader();
363 }
364
365 /**
366 * Plugin activation handler
367 *
368 * Creates database tables and performs initial setup.
369 *
370 * @since 0.0.1
371 *
372 * @return void
373 */
374 public function activate() {
375 // Create database tables.
376 require_once SURECONTACT_PLUGIN_PATH . 'includes/database/class-api-queue-table.php';
377 require_once SURECONTACT_PLUGIN_PATH . 'includes/database/class-integrations-table.php';
378 require_once SURECONTACT_PLUGIN_PATH . 'includes/database/class-abandoned-carts-table.php';
379
380 SureContact\Database\API_Queue_Table::create_table();
381 SureContact\Database\Integrations_Table::create_table();
382 SureContact\Database\Abandoned_Carts_Table::create_table();
383
384 flush_rewrite_rules();
385 }
386
387 /**
388 * Maybe create or update tables and run migrations.
389 *
390 * Runs on plugins_loaded to ensure tables exist and are up to date.
391 * Each table class handles its own version checking and update logic.
392 * Handles edge cases where activation hook didn't run (multisite, manual install, etc.).
393 *
394 * @since 0.0.1
395 *
396 * @return void
397 */
398 public function maybe_create_tables() {
399 require_once SURECONTACT_PLUGIN_PATH . 'includes/database/class-api-queue-table.php';
400 require_once SURECONTACT_PLUGIN_PATH . 'includes/database/class-integrations-table.php';
401 require_once SURECONTACT_PLUGIN_PATH . 'includes/database/class-abandoned-carts-table.php';
402 require_once SURECONTACT_PLUGIN_PATH . 'includes/class-migration-manager.php';
403
404 SureContact\Database\API_Queue_Table::maybe_create_or_update();
405 SureContact\Database\Integrations_Table::maybe_create_or_update();
406 SureContact\Database\Abandoned_Carts_Table::maybe_create_or_update();
407
408 // Run data migrations after tables are ready.
409 SureContact\Migration_Manager::maybe_run_migrations();
410 }
411
412 /**
413 * Plugin deactivation handler
414 *
415 * @since 0.0.1
416 *
417 * @return void
418 */
419 public function deactivate() {
420 flush_rewrite_rules();
421
422 // Clear SureMembers expiration schedule from both wp-cron (legacy) and
423 // Action Scheduler. The scheduler was migrated to AS in 1.4.2 but a
424 // stale wp-cron entry may still exist on installs that upgraded.
425 $timestamp = wp_next_scheduled( 'surecontact_suremembers_check_expirations' );
426 if ( $timestamp ) {
427 wp_unschedule_event( $timestamp, 'surecontact_suremembers_check_expirations' );
428 }
429 if ( function_exists( 'as_unschedule_all_actions' ) ) {
430 as_unschedule_all_actions( 'surecontact_suremembers_check_expirations', array(), 'surecontact' );
431 }
432
433 // Clear daily sync cron schedule.
434 $daily_sync_timestamp = wp_next_scheduled( SureContact\Daily_Sync_Manager::CRON_HOOK );
435 if ( $daily_sync_timestamp ) {
436 wp_unschedule_event( $daily_sync_timestamp, SureContact\Daily_Sync_Manager::CRON_HOOK );
437 }
438
439 // Unschedule abandoned cart detection.
440 SureContact\Abandoned_Cart_Manager::unschedule();
441 }
442
443 /**
444 * Add action links to plugin list
445 *
446 * @since 0.0.1
447 *
448 * @param array $links Existing plugin action links.
449 * @return array Modified plugin action links.
450 */
451 public function add_action_links( $links ) {
452 $workspace_uuid = get_option( 'surecontact_workspace_uuid', '' );
453 $is_connected = ! empty( $workspace_uuid );
454
455 $link_text = $is_connected
456 ? __( 'Access Dashboard', 'surecontact' )
457 : __( 'Get Started Now', 'surecontact' );
458
459 $dashboard_link = sprintf(
460 '<a href="%s">%s</a>',
461 esc_url( admin_url( 'admin.php?page=surecontact-dashboard' ) ),
462 esc_html( $link_text )
463 );
464
465 array_unshift( $links, $dashboard_link );
466
467 return $links;
468 }
469
470 /**
471 * Add meta links to the plugin row (under description).
472 *
473 * @since 0.0.3
474 *
475 * @param array<int,string> $links Array of plugin meta links.
476 * @param string $file Plugin file path.
477 * @return array<int,string> Modified plugin meta links.
478 */
479 public function add_meta_links( array $links, string $file ): array {
480 if ( SURECONTACT_PLUGIN_BASENAME === $file ) {
481 $stars = '';
482 for ( $indx = 0; $indx < 5; $indx++ ) {
483 $stars .= '<span class="dashicons dashicons-star-filled" style="color: #ffb900; font-size: 16px; width: 16px; height: 16px; line-height: 1.2;" aria-hidden="true"></span>';
484 }
485 $links[] = sprintf(
486 '<a href="%s" target="_blank" rel="noopener noreferrer" aria-label="%s" role="button">%s</a>',
487 esc_url( 'https://wordpress.org/support/plugin/surecontact/reviews/#new-post' ),
488 esc_attr__( 'Rate our plugin', 'surecontact' ),
489 $stars
490 );
491 }
492 return $links;
493 }
494
495 /**
496 * Get plugin directory path
497 *
498 * @since 0.0.1
499 *
500 * @return string Plugin directory path.
501 */
502 public function get_plugin_path() {
503 return $this->plugin_path;
504 }
505
506 /**
507 * Get plugin directory URL
508 *
509 * @since 0.0.1
510 *
511 * @return string Plugin directory URL.
512 */
513 public function get_plugin_url() {
514 return $this->plugin_url;
515 }
516
517 /**
518 * Get the Abandoned Cart Manager instance.
519 *
520 * @since 1.5.0
521 *
522 * @return SureContact\Abandoned_Cart_Manager|null Manager instance, or null if not yet initialized.
523 */
524 public function get_abandoned_cart_manager() {
525 return $this->abandoned_cart_manager;
526 }
527
528 /**
529 * Load Action Scheduler library
530 *
531 * @since 0.0.1
532 *
533 * @return void
534 */
535 private function load_action_scheduler() {
536 if ( function_exists( 'as_enqueue_async_action' ) ) {
537 return;
538 }
539
540 $action_scheduler_path = SURECONTACT_PLUGIN_PATH . 'lib/action-scheduler/action-scheduler.php';
541
542 if ( file_exists( $action_scheduler_path ) ) {
543 require_once $action_scheduler_path;
544 } else {
545 // Autoloader not loaded yet, use direct error_log.
546 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
547 error_log(
548 '[' . gmdate( 'Y-m-d H:i:s' ) . '] SureContact [ERROR] Plugin: Action Scheduler library not found at ' . $action_scheduler_path
549 );
550 }
551 }
552 }
553
554 // Initialize plugin.
555 $GLOBALS['surecontact'] = SureContact::get_instance();
556