PluginProbe ʕ •ᴥ•ʔ
OttoKit: All-in-One Automation Platform / 1.1.24
OttoKit: All-in-One Automation Platform v1.1.24
1.1.31 1.1.30 1.1.29 1.1.28 1.1.27 1.1.9 trunk 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 1.0.39 1.0.40 1.0.41 1.0.42 1.0.43 1.0.44 1.0.45 1.0.46 1.0.47 1.0.48 1.0.49 1.0.50 1.0.51 1.0.52 1.0.53 1.0.54 1.0.55 1.0.56 1.0.57 1.0.58 1.0.59 1.0.60 1.0.61 1.0.62 1.0.63 1.0.64 1.0.65 1.0.66 1.0.67 1.0.68 1.0.69 1.0.7 1.0.70 1.0.71 1.0.72 1.0.73 1.0.74 1.0.75 1.0.76 1.0.77 1.0.78 1.0.79 1.0.8 1.0.80 1.0.81 1.0.82 1.0.83 1.0.84 1.0.85 1.0.86 1.0.87 1.0.88 1.0.89 1.0.9 1.0.90 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8
suretriggers / src / Loader.php
suretriggers / src Last commit date
Abilities 3 months ago Admin 2 months ago Controllers 2 months ago Integrations 2 months ago Models 2 months ago Support 1 year ago Traits 3 years ago Loader.php 2 months ago
Loader.php
1031 lines
1 <?php
2 /**
3 * Loader.
4 * php version 5.6
5 *
6 * @category Loader
7 * @package SureTriggers
8 * @author BSF <username@example.com>
9 * @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
10 * @link https://www.brainstormforce.com/
11 * @since 1.0.0
12 */
13
14 namespace SureTriggers;
15
16 use DirectoryIterator;
17 use SureTriggers\Controllers\AuthController;
18 use SureTriggers\Controllers\AutomationController;
19 use SureTriggers\Controllers\EventController;
20 use SureTriggers\Controllers\GlobalSearchController;
21 use SureTriggers\Controllers\IntegrationsController;
22 use SureTriggers\Controllers\OptionController;
23 use SureTriggers\Controllers\RestController;
24 use SureTriggers\Controllers\RoutesController;
25 use SureTriggers\Controllers\WebhookRequestsController;
26 use SureTriggers\Controllers\SettingsController;
27 use SureTriggers\Traits\SingletonLoader;
28 use SureTriggers\Models\SaasApiToken;
29 use SureTriggers\Abilities\AbilitiesController;
30 use function add_menu_page;
31 use function add_submenu_page;
32
33 /**
34 * Loader
35 *
36 * @category Loader
37 * @package SureTriggers
38 * @author BSF <username@example.com>
39 * @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
40 * @link https://www.brainstormforce.com/
41 * @since 1.0.0
42 */
43 class Loader {
44
45
46
47 use SingletonLoader;
48
49 /**
50 * Constructor
51 *
52 * @since 1.0.0
53 */
54 public function __construct() {
55 register_activation_hook( SURE_TRIGGERS_FILE, [ $this, 'st_activate' ] );
56
57 $this->define_constants();
58 add_action( 'plugins_loaded', [ $this, 'load_textdomain' ] );
59 add_action( 'plugins_loaded', [ $this, 'initialize_core' ] );
60 // Admin Menu.
61 add_action( 'admin_menu', [ $this, 'admin_menu' ] );
62 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
63 add_action( 'admin_head', [ $this, 'add_admin_menu_styles' ] );
64 add_action( 'admin_init', [ $this, 'reset_plugin' ] );
65
66 add_filter( 'plugin_action_links_' . plugin_basename( SURE_TRIGGERS_FILE ), [ $this, 'add_settings_link' ] );
67 add_action( 'admin_init', [ $this, 'redirect_after_activation' ] );
68
69 add_action( 'admin_notices', [ $this, 'display_notice' ] );
70
71 add_action( 'all_admin_notices', [ $this, 'suretriggers_show_api_connection_error' ] );
72
73 add_action( 'wp_dashboard_setup', [ $this, 'add_dashboard_widgets' ] );
74
75 // Remove Webhook Requests retry cron and requests table.
76 register_uninstall_hook(
77 SURE_TRIGGERS_FILE,
78 [ WebhookRequestsController::class, 'suretriggers_remove_table_retry_cron' ]
79 );
80 }
81
82 /**
83 * Adding dashboard widget.
84 *
85 * @return void
86 */
87 public function add_dashboard_widgets() {
88 if ( isset( OptionController::$options['secret_key'] ) ) {
89 return;
90 }
91
92 wp_add_dashboard_widget(
93 'suretriggers_dashboard_widget',
94 __( 'Stop Doing It Manually!', 'suretriggers' ),
95 [ $this, 'dashboard_widget_display' ],
96 null,
97 null,
98 'side',
99 'high'
100 );
101 }
102
103 /**
104 * Dashboard widget callback.
105 *
106 * @return void
107 */
108 public function dashboard_widget_display() { ?>
109 <div>
110 <p> <?php esc_html_e( 'Automation That’s Easy Enough for Anyone.', 'suretriggers' ); ?></p>
111 <p>
112 <?php
113 esc_html_e(
114 'OttoKit connects all your tools - WooCommerce, Forms, CRM, and more, so your website runs smoothly while you focus on your business.
115 ',
116 'suretriggers'
117 );
118 ?>
119 </p>
120 <a href="<?php echo esc_url( admin_url( 'admin.php?page=suretriggers' ) ); ?>" class="button button-primary"> <?php esc_html_e( 'Start Automating', 'suretriggers' ); ?> </a>
121 </div>
122 <?php
123 }
124
125 /**
126 * Load Plugin Text Domain.
127 * This will load the translation textdomain depending on the file priorities.
128 * 1. Global Languages /wp-content/languages/suretriggers/ folder
129 * 2. Local directory /wp-content/plugins/suretriggers/languages/ folder
130 *
131 * @return void
132 */
133 public function load_textdomain() {
134 // Default languages directory.
135 $lang_dir = SURE_TRIGGERS_DIR . 'languages/';
136
137 /**
138 * Filters the languages directory path to use for plugin.
139 *
140 * @param string $lang_dir The languages directory path.
141 */
142 $lang_dir = apply_filters( 'suretriggers_languages_directory', $lang_dir );
143
144 // Traditional WordPress plugin locale filter.
145 global $wp_version;
146
147 $get_locale = get_locale();
148
149 if ( $wp_version >= 4.7 ) {
150 $get_locale = get_user_locale();
151 }
152
153 /**
154 * Language Locale for plugin
155 *
156 * Uses get_user_locale()` in WordPress 4.7 or greater,
157 * otherwise uses `get_locale()`.
158 */
159 $locale = apply_filters( 'plugin_locale', $get_locale, 'suretriggers' );//phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- wordpress hook
160 $mofile = sprintf( '%1$s-%2$s.mo', 'suretriggers', $locale );
161
162 // Setup paths to current locale file.
163 $mofile_global = WP_LANG_DIR . '/plugins/' . $mofile;
164 $mofile_local = $lang_dir . $mofile;
165
166 $mofile_global = wp_normalize_path( $mofile_global );
167 $mofile_local = wp_normalize_path( $mofile_local );
168
169 if ( file_exists( $mofile_global ) && wp_is_file_mod_allowed( 'load_textdomain_mofile' ) ) {
170 // Look in global /wp-content/languages/suretriggers/ folder.
171 load_textdomain( 'suretriggers', $mofile_global );
172 } elseif ( file_exists( $mofile_local ) && wp_is_file_mod_allowed( 'load_textdomain_mofile' ) && $this->st_valid_file_path( $mofile_local ) ) {
173 // Look in local /wp-content/plugins/suretriggers/languages/ folder.
174 load_textdomain( 'suretriggers', $mofile_local );
175 } else {
176 load_plugin_textdomain( 'suretriggers', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
177 }
178 }
179
180 /**
181 * Check if file is valid.
182 *
183 * @param string $file_path file_path.
184 * @return bool
185 */
186 private function st_valid_file_path( $file_path ) {
187 $allowed_dir = realpath( SURE_TRIGGERS_DIR . 'languages/' );
188 $real_file_path = realpath( $file_path );
189 return false !== $real_file_path && $allowed_dir && 0 === strpos( $real_file_path, $allowed_dir );
190 }
191
192 /**
193 * Display notice.
194 *
195 * @return void
196 */
197 public function display_notice() {
198 global $pagenow;
199 if ( isset( OptionController::$options['secret_key'] ) ) {
200 return;
201 }
202 if ( 'index.php' != $pagenow ) {
203 return;
204 }
205 ?>
206 <div class="notice notice-success" style="padding-bottom: 15px;">
207 <p>
208 <strong>
209 <?php esc_html_e( 'Automate Your WordPress Site. Save Hours. Earn More.', 'suretriggers' ); ?>
210 <span style="transform: rotate(-90deg); font-size: 15px;" class="dashicons dashicons-admin-plugins"></span>
211 </strong>
212 </p>
213 <p> <?php esc_html_e( 'OttoKit connects your plugins and favorite apps so your business runs on autopilot — while you focus on what matters most.', 'suretriggers' ); ?> </p>
214
215 <a href="<?php echo esc_url( admin_url( 'admin.php?page=suretriggers' ) ); ?>" class="button button-primary"> <?php esc_html_e( 'Start Automating', 'suretriggers' ); ?> </a>
216 <a href="https://ottokit.com/?utm_source=wpplugin&utm_medium=dashboard&utm_campaign=top+bar" class="button button-secondary"> <?php esc_html_e( 'See How It Works', 'suretriggers' ); ?> </a>
217 </div>
218 <?php
219 }
220
221 /**
222 * Show Connection Error Admin Notice.
223 *
224 * @return void
225 */
226 public function suretriggers_show_api_connection_error() {
227 global $pagenow;
228 $is_authorized = true;
229 if ( ! current_user_can( 'manage_options' ) ) {
230 $is_authorized = self::suretriggers_user_permission_check();
231 }
232 if ( 'index.php' != $pagenow || ! isset( OptionController::$options['secret_key'] ) || ! $is_authorized ) {
233 return;
234 }
235 $notice = get_option( 'suretriggers_verify_connection' );
236 // If empty option value for connection status, then verify the connection.
237 if ( empty( $notice ) || 'suretriggers_connection_successful' != $notice ) {
238 $connection_status = RestController::suretriggers_verify_wp_connection();
239 $connection_status_code = wp_remote_retrieve_response_code( $connection_status );
240 if ( is_wp_error( $connection_status ) ) {
241 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_wp_error' );
242 } else {
243 if ( 200 !== $connection_status_code ) {
244 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_error' );
245 } else {
246 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_successful' );
247 }
248 }
249 }
250 $notice = get_option( 'suretriggers_verify_connection' );
251 if ( 'suretriggers_connection_successful' != $notice ) {
252 // If connection status is not successful, then show the notice.
253 ?>
254 <div class="notice notice-error is-dismissible">
255 <p>
256 <strong>
257 <?php esc_html_e( 'OttoKit Connection Issue', 'suretriggers' ); ?>
258 <span style="transform: rotate(-180deg); font-size: 20px;" class="dashicons dashicons-warning"></span>
259 </strong>
260 </p>
261 <p>
262 <?php esc_html_e( 'There is an issue with the established connection between WordPress and OttoKit. Please visit the OttoKit dashboard to verify and re-establish the connection if necessary.', 'suretriggers' ); ?>
263 </p>
264 <p>
265 <a href="<?php echo esc_url( admin_url( 'admin.php?page=suretriggers' ) ); ?>" class="button button-secondary"> <?php esc_html_e( 'Go To OttoKit', 'suretriggers' ); ?> </a>
266 </p>
267 </div>
268 <?php
269 }
270 }
271
272 /**
273 * Redirect user after plugin activation.
274 *
275 * @return void
276 */
277 public function redirect_after_activation() {
278 $is_redirect = get_transient( 'st-redirect-after-activation' );
279 if ( $is_redirect ) {
280 delete_transient( 'st-redirect-after-activation' );
281 $url = get_admin_url() . 'admin.php?page=suretriggers';
282 wp_safe_redirect( $url );
283 die;
284 }
285 }
286
287 /**
288 * Adding setting link.
289 *
290 * @param array $links links.
291 * @return array
292 */
293 public function add_settings_link( array $links ) {
294 $url = get_admin_url() . 'admin.php?page=suretriggers';
295 $setting_option = get_option( 'suretrigger_options' );
296 if ( isset( $setting_option ) && ! empty( $setting_option ) ) {
297 $settings_link = '<a href="' . $url . '">' . __( 'Dashboard', 'suretriggers' ) . '</a>';
298 } else {
299 $settings_link = '<a href="' . $url . '">' . __( 'Connect', 'suretriggers' ) . '</a>';
300 }
301 $links[] = $settings_link;
302
303 // Add Get OttoKit Pro link for free and pro users.
304 $verification_data = get_option( 'suretriggers_lifetime_user_plan_data' );
305 $plan_id = is_array( $verification_data ) && isset( $verification_data['plan_id'] ) ? $verification_data['plan_id'] : '';
306 if ( in_array( $plan_id, [ 'free', 'pro' ], true ) ) {
307 $button_text = ( 'free' === $plan_id ) ? __( 'Get OttoKit Pro', 'suretriggers' ) : __( 'Upgrade', 'suretriggers' );
308 $upgrade_link = '<a href="https://ottokit.com/pricing/?utm_source=wpplugin&utm_medium=plugin+list&utm_campaign=plugin+list" target="_blank" style="color: #28a745; font-weight: bold;">' . $button_text . '</a>';
309 $links[] = $upgrade_link;
310 }
311
312 return $links;
313 }
314
315 /**
316 * Define constants
317 *
318 * @return void
319 * @since 1.0.0
320 */
321 public function define_constants() {
322 $sass_url = 'https://app.ottokit.com';
323 $api_url = 'https://api.ottokit.com';
324 $webhook_url = 'https://webhook.ottokit.com';
325
326 define( 'SURE_TRIGGERS_BASE', plugin_basename( SURE_TRIGGERS_FILE ) );
327 define( 'SURE_TRIGGERS_DIR', plugin_dir_path( SURE_TRIGGERS_FILE ) );
328 define( 'SURE_TRIGGERS_URL', plugins_url( '/', SURE_TRIGGERS_FILE ) );
329 define( 'SURE_TRIGGERS_VER', '1.1.24' );
330 define( 'SURE_TRIGGERS_DB_VER', '1.1.24' );
331 define( 'SURE_TRIGGERS_REST_NAMESPACE', 'sure-triggers/v1' );
332 define( 'SURE_TRIGGERS_SASS_URL', $sass_url . '/wp-json/wp-plugs/v1/' );
333 define( 'SURE_TRIGGERS_SITE_URL', $sass_url );
334 define( 'SURE_TRIGGERS_API_SERVER_URL', $api_url );
335 define( 'SURE_TRIGGERS_WEBHOOK_SERVER_URL', $webhook_url );
336
337 define( 'SURE_TRIGGERS_PAGE', 'OttoKit' );
338 define( 'SURE_TRIGGERS_AS_GROUP', 'OttoKit' );
339
340 define( 'SURE_TRIGGERS_ACTION_ERROR_MESSAGE', 'An unexpected error occurred. Something went wrong with the action.' );
341 }
342
343 /**
344 * Flush permalink rules while plugin activation.
345 *
346 * @return void
347 */
348 public function st_activate() {
349 flush_rewrite_rules(); //phpcs:ignore
350
351 set_transient( 'st-redirect-after-activation', true, 120 );
352 }
353
354 /**
355 * SureTriggers Access for Users and User Roles.
356 *
357 * @return bool
358 */
359 private function suretriggers_user_permission_check() {
360 $current_user = wp_get_current_user();
361 $current_user_id = $current_user->ID;
362 $current_user_roles = $current_user->roles;
363
364 // Get enabled users and roles with proper default values.
365 $enabled_users = get_option( 'suretriggers_enabled_users', [] );
366 $enabled_user_roles = get_option( 'suretriggers_enabled_user_roles', [] );
367
368 $enabled_users = is_array( $enabled_users ) ? $enabled_users : [];
369 $enabled_user_roles = is_array( $enabled_user_roles ) ? $enabled_user_roles : [];
370
371 $is_authorized = in_array( $current_user_id, (array) $enabled_users ) || array_intersect( $current_user_roles, (array) $enabled_user_roles );
372
373 return $is_authorized;
374 }
375
376 /**
377 * Add main menu
378 *
379 * @since x.x.x
380 *
381 * @return void
382 */
383 public function admin_menu() {
384 $page_title = apply_filters( 'st_menu_page_title', esc_html__( 'OttoKit', 'suretriggers' ) );
385 $logo = file_get_contents( plugin_dir_path( SURE_TRIGGERS_FILE ) . 'assets/images/OttoKitLogo.svg' );
386
387 $is_authorized = true;
388 if ( ! current_user_can( 'manage_options' ) ) {
389 $is_authorized = self::suretriggers_user_permission_check();
390 }
391
392 if ( $is_authorized ) {
393 add_menu_page(
394 $page_title,
395 $page_title,
396 'read',
397 'suretriggers',
398 [ $this, 'menu_callback' ],
399 $logo ? 'data:image/svg+xml;base64,' . base64_encode( $logo ) : '',
400 30.6002
401 );
402
403 // Replace the auto-created first submenu item (which duplicates the parent title)
404 // with an explicit "Dashboard" item using the same slug.
405 add_submenu_page(
406 'suretriggers',
407 __( 'Dashboard', 'suretriggers' ),
408 __( 'Dashboard', 'suretriggers' ),
409 'read',
410 'suretriggers',
411 [ $this, 'menu_callback' ]
412 );
413
414 // Add quick-access submenu items that deep-link into the SaaS dashboard.
415 // The React app reads redirect_url from the URL and forwards it to the iframe.
416 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Standard pattern for adding direct-link submenu items.
417 global $submenu;
418 $deep_link_items = [
419 'workflows' => __( 'Workflows', 'suretriggers' ),
420 'tables' => __( 'Tables', 'suretriggers' ),
421 'forms' => __( 'Forms', 'suretriggers' ),
422 'mcp' => __( 'MCP', 'suretriggers' ),
423 'recipes' => __( 'Recipes', 'suretriggers' ),
424 'folders' => __( 'Folders', 'suretriggers' ),
425 'email-templates' => __( 'Email Templates', 'suretriggers' ),
426 'variables' => __( 'Variables', 'suretriggers' ),
427 'history' => __( 'History', 'suretriggers' ),
428 'apps' => __( 'Apps', 'suretriggers' ),
429 'members' => __( 'Members', 'suretriggers' ),
430 'settings' => __( 'Settings', 'suretriggers' ),
431 ];
432
433 // Items restricted to internal BSF users only (checked against SaaS connected email).
434 $bsf_only_items = [ 'tables', 'forms', 'variables' ];
435 $connected_email = OptionController::get_option( 'connected_email_key' );
436 $is_bsf_user = is_string( $connected_email ) && ! empty( $connected_email ) && '@bsf.io' === substr( $connected_email, -7 );
437
438 foreach ( $deep_link_items as $path => $label ) {
439 if ( in_array( $path, $bsf_only_items, true ) && ! $is_bsf_user ) {
440 continue;
441 }
442 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
443 $submenu['suretriggers'][] = [
444 $label,
445 'read',
446 admin_url( 'admin.php?page=suretriggers&redirect_url=/' . $path ),
447 ];
448 }
449
450 add_submenu_page(
451 'suretriggers',
452 __( 'OttoKit Status', 'suretriggers' ),
453 __( 'Status', 'suretriggers' ),
454 'read',
455 'suretriggers-status',
456 [ $this, 'suretriggers_status_menu_callback' ]
457 );
458
459 // Add Get OttoKit Pro menu for free and pro users.
460 $verification_data = get_option( 'suretriggers_lifetime_user_plan_data' );
461 $plan_id = is_array( $verification_data ) && isset( $verification_data['plan_id'] ) ? $verification_data['plan_id'] : '';
462 if ( in_array( $plan_id, [ 'free', 'pro' ], true ) ) {
463 $button_text = ( 'free' === $plan_id ) ? __( 'Get OttoKit Pro', 'suretriggers' ) : __( 'Upgrade', 'suretriggers' );
464 add_submenu_page(
465 'suretriggers',
466 $button_text,
467 '<span class="ottokit-upgrade-btn">' . $button_text . '</span>',
468 'read',
469 'suretriggers-upgrade-plan',
470 [ $this, 'suretriggers_upgrade_plan_callback' ]
471 );
472 }
473 }
474
475 if ( isset( OptionController::$options['secret_key'] ) ) {
476 add_options_page(
477 __( 'OttoKit Settings', 'suretriggers' ),
478 __( 'OttoKit Settings', 'suretriggers' ),
479 'manage_options',
480 'ottokit-settings',
481 [ $this, 'suretriggers_render_interface_callback' ]
482 );
483 }
484 }
485
486 /**
487 * Enqueue the admin scripts
488 *
489 * @param string $hook hook.
490 * @since x.x.x
491 *
492 * @return void
493 */
494 public function enqueue_scripts( $hook = '' ) {
495 // Always enqueue admin CSS for upgrade button styling.
496 wp_enqueue_style( 'st-trigger-style', SURE_TRIGGERS_URL . 'assets/admin-css/st-admin-css.css', [], SURE_TRIGGERS_VER );
497
498 if ( ! in_array( $hook, [ 'toplevel_page_suretriggers', 'ottokit_page_suretriggers-status', 'settings_page_ottokit-settings' ], true ) ) {
499 return;
500 }
501
502 remove_all_actions( 'admin_notices' );
503
504 $file = SURE_TRIGGERS_DIR . 'app/build/main.asset.php';
505 if ( ! file_exists( $file ) ) {
506 return;
507 }
508
509 $asset = require_once $file;
510
511 if ( ! isset( $asset ) ) {
512 return;
513 }
514
515 wp_register_script(
516 'sure-trigger-admin',
517 SURE_TRIGGERS_URL . 'app/build/main.js',
518 array_merge( $asset['dependencies'], [ 'regenerator-runtime' ], [ 'wp-i18n' ] ),
519 $asset['version'],
520 true
521 );
522
523 wp_localize_script(
524 'sure-trigger-admin',
525 'sureTriggerData',
526 $this->get_localized_array()
527 );
528 wp_enqueue_script( 'sure-trigger-admin' );
529 // Set the script translations.
530 wp_set_script_translations( 'sure-trigger-admin', 'suretriggers', SURE_TRIGGERS_DIR . 'languages' );
531 wp_enqueue_style( 'sure-trigger-components', SURE_TRIGGERS_URL . 'app/build/style-main.css', [], SURE_TRIGGERS_VER );
532 wp_enqueue_style( 'sure-trigger-css', SURE_TRIGGERS_URL . 'app/build/main.css', [], SURE_TRIGGERS_VER );
533 }
534
535 /**
536 * Get localized array for sure triggers.
537 *
538 * @return array
539 */
540 private function get_localized_array() {
541 $settings_nonce = wp_create_nonce( 'suretriggers_settings_nonce_action' );
542 $current_user = wp_get_current_user();
543 global $wp_roles;
544 $is_authorized = true;
545 if ( ! current_user_can( 'manage_options' ) ) {
546 $is_authorized = self::suretriggers_user_permission_check();
547 }
548
549 $source_type = get_option( 'suretriggers_source' );
550
551 // Get enabled users and roles with proper default values.
552 $enabled_users = get_option( 'suretriggers_enabled_users', [] );
553 $enabled_user_roles = get_option( 'suretriggers_enabled_user_roles', [] );
554
555 $enabled_users = is_array( $enabled_users ) ? $enabled_users : [];
556 $enabled_user_roles = is_array( $enabled_user_roles ) ? $enabled_user_roles : [];
557
558 $data = [
559 'siteContent' => [
560 'siteUrl' => str_replace( '/wp-json/', '', get_rest_url() ),
561 'redirectUrl' => get_site_url() . '/wp-admin/themes.php?page=suretriggers',
562 'connectNonce' => wp_create_nonce( 'sure-trigger-connect' ),
563 'connectUrl' => SURE_TRIGGERS_SITE_URL . '/connect-st/connect',
564 'siteTitle' => get_bloginfo( 'name' ),
565 'resetUrl' => base64_encode( wp_nonce_url( admin_url( 'admin.php?st-reset=true' ), 'st-reset-action' ) ),
566 'sourceType' => $source_type,
567 'adminLanguage' => get_user_locale(),
568 ],
569 'user' => [
570 'name' => $current_user->display_name,
571 'email' => $current_user->user_email,
572 ],
573 'stSaasURL' => trailingslashit( SURE_TRIGGERS_SITE_URL ),
574 'stPluginURL' => plugin_dir_url( SURE_TRIGGERS_FILE ),
575 'integrations' => IntegrationsController::get_activated_integrations(),
576 'enabledIntegrations' => OptionController::get_option( 'enabled_integrations' ),
577 'verification_status' => false,
578 'projects' => [],
579 'apiSlug' => SURE_TRIGGERS_REST_NAMESPACE,
580 'reConnectSorryMsg' => (bool) OptionController::get_option( 'st_connect_notice_deprecated' ),
581 'usersRoles' => array_diff_key( $wp_roles->get_names(), [ 'administrator' => '' ] ),
582 'usersList' => get_users(
583 [
584 'fields' => [
585 'ID',
586 'display_name',
587 ],
588 'role__not_in' => [
589 'administrator',
590 ],
591 ]
592 ),
593 'enabledUsers' => $enabled_users,
594 'enabledUserRoles' => $enabled_user_roles,
595 ];
596
597 if ( $is_authorized ) {
598 $data['siteContent']['accessKey'] = SaasApiToken::get();
599 $data['siteContent']['connected_email'] = OptionController::get_option( 'connected_email_key' );
600 }
601
602 $data['settingsNonce'] = esc_js( $settings_nonce );
603 $data['ajaxurl'] = esc_url( admin_url( 'admin-ajax.php' ) );
604
605 return apply_filters( 'sure_trigger_control_localize_vars', $data );
606 }
607
608 /**
609 * Render OttoKit Interface callback.
610 *
611 * @return void
612 */
613 public function suretriggers_render_interface_callback() {
614 ?>
615 <div id="ottokit-settings-page" class="ottokit-settings"></div>
616 <?php
617 }
618
619 /**
620 * Menu callback.
621 *
622 * @since x.x.x
623 *
624 * @return void
625 */
626 public function menu_callback() {
627 // Check permalink structure first.
628 $permalink_structure = get_option( 'permalink_structure' );
629 $is_plain_permalink = empty( $permalink_structure );
630
631 // If permalink structure is "Plain" and we're trying to connect or already connected, show a prominent warning.
632 if ( $is_plain_permalink ) {
633 ?>
634 <div class="st-permalink-warning-wrapper">
635 <div class="st-permalink-warning-card">
636 <div class="st-permalink-warning-icon">
637 <span class="st-permalink-warning-icon-text">!</span>
638 </div>
639 <div class="st-permalink-warning-content">
640 <p class="st-permalink-warning-title">
641 <?php esc_html_e( '“Plain” Permalink Structure Detected', 'suretriggers' ); ?>
642 </p>
643 <p class="st-permalink-warning-message">
644 <?php esc_html_e( 'Your site is currently using the “Plain” permalink structure, which is not supported by OttoKit. Please visit', 'suretriggers' ); ?>
645 <a href="<?php echo esc_url( admin_url( 'options-permalink.php' ) ); ?>" target="_blank" class="st-permalink-warning-link">
646 <?php esc_html_e( 'Permalinks Settings', 'suretriggers' ); ?>
647 </a>
648 <?php esc_html_e( 'on your WordPress Dashboard and choose any structure other than “Plain”.', 'suretriggers' ); ?>
649 </p>
650 </div>
651 </div>
652 </div>
653 <?php
654 }
655
656 // Verify Token.
657 $response = RestController::verify_user_token();
658 $response_body = wp_remote_retrieve_body( $response );
659 $response_code = wp_remote_retrieve_response_code( $response );
660 if ( ! empty( $response_body ) ) {
661 $response_body = json_decode( $response_body, true );
662 }
663 if ( 200 === $response_code || 401 === $response_code ) {
664 if ( is_array( $response_body ) && isset( $response_body['is_iframe_enabled'] ) && 'NO' === $response_body['is_iframe_enabled'] ) {
665 ?>
666 <div class="suretriggers-nobase">
667 <div>
668 <div>
669 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check inline-block h-8 w-8 text-green-400 mb-6" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg>
670 <h2 class="suretriggers-info-title">
671 <?php esc_html_e( 'OttoKit is connected.', 'suretriggers' ); ?>
672 </h2>
673 <p class="suretriggers-info-content">
674 <?php esc_html_e( 'Your WordPress site is successfully connected to the OttoKit SaaS platform. However, the OttoKit interface display is currently disabled. Click below to enable it.', 'suretriggers' ); ?>
675 </p>
676 <a class="suretriggers-info-link" href="<?php echo esc_url( SURE_TRIGGERS_SITE_URL . '/apps/WordPress' ); ?>" target="_blank">
677 <?php esc_html_e( 'Access Connection Page', 'suretriggers' ); ?>
678 </a>
679 </div>
680 </div>
681 </div>
682 <?php
683 } else {
684 ?>
685 <div id="sure-triggger-entry" class="st-base"></div>
686 <?php
687 }
688 } elseif ( isset( $response ) && is_wp_error( $response ) || 200 !== $response_code ) {
689 ?>
690 <div class="suretriggers-nobase">
691 <div>
692 <div>
693 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30" height="24" viewBox="0 0 122.88 122.879" enable-background="new 0 0 122.88 122.879" xml:space="preserve" class="lucide lucide-circle-check inline-block h-8 w-8 text-green-400 mb-6"><g><path fill="#FF4141" d="M61.44,0c16.96,0,32.328,6.882,43.453,17.986c11.104,11.125,17.986,26.494,17.986,43.453 c0,16.961-6.883,32.328-17.986,43.453C93.769,115.998,78.4,122.879,61.44,122.879c-16.96,0-32.329-6.881-43.454-17.986 C6.882,93.768,0,78.4,0,61.439C0,44.48,6.882,29.111,17.986,17.986C29.112,6.882,44.48,0,61.44,0L61.44,0z M73.452,39.152 c2.75-2.792,7.221-2.805,9.986-0.026c2.764,2.776,2.775,7.292,0.027,10.083L71.4,61.445l12.077,12.25 c2.728,2.77,2.689,7.256-0.081,10.021c-2.772,2.766-7.229,2.758-9.954-0.012L61.445,71.541L49.428,83.729 c-2.75,2.793-7.22,2.805-9.985,0.025c-2.763-2.775-2.776-7.291-0.026-10.082L51.48,61.435l-12.078-12.25 c-2.726-2.769-2.689-7.256,0.082-10.022c2.772-2.765,7.229-2.758,9.954,0.013L61.435,51.34L73.452,39.152L73.452,39.152z M96.899,25.98C87.826,16.907,75.29,11.296,61.44,11.296c-13.851,0-26.387,5.611-35.46,14.685 c-9.073,9.073-14.684,21.609-14.684,35.459s5.611,26.387,14.684,35.459c9.073,9.074,21.609,14.686,35.46,14.686 c13.85,0,26.386-5.611,35.459-14.686c9.073-9.072,14.684-21.609,14.684-35.459S105.973,35.054,96.899,25.98L96.899,25.98z"></path></g></svg>
694 <h2 class="suretriggers-info-title">
695 <?php esc_html_e( 'OttoKit Not Connected.', 'suretriggers' ); ?>
696 </h2>
697 <p class="suretriggers-info-content">
698 <?php esc_html_e( 'It looks like your WordPress site’s connection with OttoKit has been affected because the URL used for communication has changed. The current link for your site is different from the one OttoKit was originally connected to.', 'suretriggers' ); ?>
699 </p>
700 <a class="suretriggers-info-link" href="<?php echo esc_url( SURE_TRIGGERS_SITE_URL ); ?>" target="_blank">
701 <?php esc_html_e( 'Access Dashboard', 'suretriggers' ); ?>
702 </a>
703 <a class="suretriggers-info-link" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?st-reset=true' ), 'st-reset-action' ) ); ?>">
704 <?php esc_html_e( 'Disconnect OttoKit', 'suretriggers' ); ?>
705 </a>
706 </div>
707 </div>
708 </div>
709 <?php
710 }
711 }
712
713 /**
714 * Status Menu callback.
715 *
716 * @since x.x.x
717 *
718 * @return void
719 */
720 public function suretriggers_status_menu_callback() {
721 if ( ! isset( OptionController::$options['secret_key'] ) ) {
722 ?>
723 <div class="suretriggers-nobase">
724 <div>
725 <div>
726 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30" height="24" viewBox="0 0 122.88 122.879" enable-background="new 0 0 122.88 122.879" xml:space="preserve" class="lucide lucide-circle-check inline-block h-8 w-8 text-green-400 mb-6"><g><path fill="#FF4141" d="M61.44,0c16.96,0,32.328,6.882,43.453,17.986c11.104,11.125,17.986,26.494,17.986,43.453 c0,16.961-6.883,32.328-17.986,43.453C93.769,115.998,78.4,122.879,61.44,122.879c-16.96,0-32.329-6.881-43.454-17.986 C6.882,93.768,0,78.4,0,61.439C0,44.48,6.882,29.111,17.986,17.986C29.112,6.882,44.48,0,61.44,0L61.44,0z M73.452,39.152 c2.75-2.792,7.221-2.805,9.986-0.026c2.764,2.776,2.775,7.292,0.027,10.083L71.4,61.445l12.077,12.25 c2.728,2.77,2.689,7.256-0.081,10.021c-2.772,2.766-7.229,2.758-9.954-0.012L61.445,71.541L49.428,83.729 c-2.75,2.793-7.22,2.805-9.985,0.025c-2.763-2.775-2.776-7.291-0.026-10.082L51.48,61.435l-12.078-12.25 c-2.726-2.769-2.689-7.256,0.082-10.022c2.772-2.765,7.229-2.758,9.954,0.013L61.435,51.34L73.452,39.152L73.452,39.152z M96.899,25.98C87.826,16.907,75.29,11.296,61.44,11.296c-13.851,0-26.387,5.611-35.46,14.685 c-9.073,9.073-14.684,21.609-14.684,35.459s5.611,26.387,14.684,35.459c9.073,9.074,21.609,14.686,35.46,14.686 c13.85,0,26.386-5.611,35.459-14.686c9.073-9.072,14.684-21.609,14.684-35.459S105.973,35.054,96.899,25.98L96.899,25.98z"></path></g></svg>
727 <h2 class="suretriggers-info-title">
728 <?php esc_html_e( 'OttoKit Not Connected.', 'suretriggers' ); ?>
729 </h2>
730 <p class="suretriggers-info-content">
731 <?php esc_html_e( 'Please connect your OttoKit account to access registered events and outgoing requests.', 'suretriggers' ); ?>
732 </p>
733 <a href="<?php echo esc_url( admin_url( 'admin.php?page=suretriggers' ) ); ?>" class="suretriggers-info-link"> <?php esc_html_e( 'Connect With OttoKit', 'suretriggers' ); ?> </a>
734 </div>
735 </div>
736 </div>
737 <?php
738 return;
739 }
740 ?>
741 <div class="wrap">
742 <?php
743 $tabs = [
744 'st_system_page' => __( 'Status', 'suretriggers' ),
745 'st_outgoing_requests' => __( 'Outgoing Requests', 'suretriggers' ),
746 'st_troubleshooting' => __( 'Troubleshooting', 'suretriggers' ),
747 ];
748 $current_tab = 'st_system_page';
749 if ( isset( $_REQUEST['tab'], $_REQUEST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'suretriggers_tab_nonce' ) ) {
750 if ( array_key_exists( sanitize_key( $_REQUEST['tab'] ), $tabs ) ) {
751 $current_tab = sanitize_key( $_REQUEST['tab'] );
752 }
753 }
754 ?>
755 <nav class="suretriggers-nav-tab nav-tab-wrapper">
756 <?php
757 foreach ( $tabs as $name => $label ) {
758 $tab_url = add_query_arg(
759 [
760 'tab' => $name,
761 '_wpnonce' => wp_create_nonce( 'suretriggers_tab_nonce' ),
762 ],
763 admin_url( 'admin.php?page=suretriggers-status' )
764 );
765 echo '<a href="' . esc_url( $tab_url ) . '" class="nav-tab ';
766 if ( $current_tab == $name ) {
767 echo 'nav-tab-active';
768 }
769 echo '">' . esc_html( $label ) . '</a>';
770 }
771 ?>
772 </nav>
773 <?php
774 switch ( $current_tab ) {
775 case 'st_system_page':
776 include_once __DIR__ . '/Admin/Views/st-admin-system-page.php';
777 break;
778 case 'st_outgoing_requests':
779 include_once __DIR__ . '/Admin/Views/st-admin-outgoing-req-page.php';
780 break;
781 case 'st_troubleshooting':
782 include_once __DIR__ . '/Admin/Views/st-admin-troubleshooting-page.php';
783 break;
784 }
785 ?>
786 </div>
787 <?php
788 }
789
790 /**
791 * Get OttoKit Pro Menu callback.
792 *
793 * @since x.x.x
794 *
795 * @return void
796 */
797 public function suretriggers_upgrade_plan_callback() {
798 ?>
799 <script type="text/javascript">
800 window.location.href = 'https://ottokit.com/pricing/?utm_source=wpplugin&utm_medium=menu&utm_campaign=left';
801 </script>
802 <?php
803 exit;
804 }
805
806 /**
807 * Add admin menu styles.
808 *
809 * @since x.x.x
810 *
811 * @return void
812 */
813 public function add_admin_menu_styles() {
814 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check for menu highlighting.
815 $redirect_url_raw = isset( $_GET['redirect_url'] ) ? sanitize_text_field( wp_unslash( $_GET['redirect_url'] ) ) : '';
816 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
817 $current_page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
818
819 // Validate redirect_url against known deep-link paths to prevent CSS injection.
820 $allowed_paths = [ '/workflows', '/tables', '/forms', '/mcp', '/recipes', '/folders', '/email-templates', '/variables', '/history', '/apps', '/members', '/settings' ];
821 $redirect_url = in_array( $redirect_url_raw, $allowed_paths, true ) ? $redirect_url_raw : '';
822 ?>
823 <style>
824 <?php if ( 'suretriggers' === $current_page && '' === $redirect_url ) : ?>
825 /* Left border indicator for Dashboard when it's the active page */
826 #toplevel_page_suretriggers .wp-submenu li.wp-first-item.current a {
827 border-left: 3px solid #fff !important;
828 padding-left: 9px !important;
829 }
830 <?php endif; ?>
831 <?php if ( 'suretriggers' === $current_page && '' !== $redirect_url ) : ?>
832 /* Remove default highlight from Dashboard when a deep-link item is active */
833 #toplevel_page_suretriggers .wp-submenu li.wp-first-item.current a {
834 color: #b4b9be !important;
835 font-weight: 400 !important;
836 }
837 #toplevel_page_suretriggers .wp-submenu li.wp-first-item.current {
838 /* Keep class for WP internals but override visual */
839 }
840 /* Left border indicator for the active deep-link submenu item */
841 <?php // phpcs:disable WordPressVIPMinimum.Security.ProperEscapingFunction.hrefSrcEscUrl -- CSS attribute selector, not HTML href. Value is allowlisted. ?>
842 #toplevel_page_suretriggers .wp-submenu li a[href*="redirect_url=<?php echo esc_attr( rawurlencode( $redirect_url ) ); ?>"],
843 #toplevel_page_suretriggers .wp-submenu li a[href*="redirect_url=<?php echo esc_attr( $redirect_url ); ?>"] {
844 <?php // phpcs:enable WordPressVIPMinimum.Security.ProperEscapingFunction.hrefSrcEscUrl ?>
845 color: #fff !important;
846 font-weight: 600 !important;
847 border-left: 3px solid #fff !important;
848 padding-left: 9px !important;
849 }
850 <?php endif; ?>
851 </style>
852 <script>
853 jQuery(document).ready(function($) {
854 // Direct redirect for admin menu upgrade button.
855 $('a[href*="suretriggers-lifetime-access"]').on('click', function(e) {
856 e.preventDefault();
857 window.open('https://ottokit.com/pricing/?utm_source=wpplugin&utm_medium=menu&utm_campaign=left', '_blank');
858 return false;
859 });
860 });
861 </script>
862 <?php
863 }
864
865 /**
866 * Include all files from the folder.
867 *
868 * @param string $folder folder path.
869 * @return void
870 */
871 public function include_all_files( $folder ) {
872 $dir = new DirectoryIterator( $folder );
873 foreach ( $dir as $file ) {
874 if ( ! $file->isDot() ) {
875 if ( $file->isDir() ) {
876 $this->include_all_files( $file->getPathname() );
877 } else {
878 require_once $file->getPathname();
879 }
880 }
881 }
882 }
883
884 /**
885 * Initialize core trigger and actions.
886 *
887 * @return void
888 */
889 public function initialize_core() {
890
891 IntegrationsController::load_event_files();
892
893 EventController::get_instance();
894 IntegrationsController::get_instance();
895 GlobalSearchController::get_instance();
896 RestController::get_instance();
897 OptionController::get_instance();
898 AutomationController::get_instance();
899 AuthController::get_instance();
900 RoutesController::get_instance();
901 WebhookRequestsController::get_instance();
902 SettingsController::get_instance();
903 AbilitiesController::get_instance();
904
905 // SureTriggers Custom Filter data.
906 add_filter( 'suretriggers_get_iframe_url', [ $this, 'suretriggers_iframe_data' ] );
907 add_filter( 'suretriggers_is_user_connected', [ $this, 'suretriggers_saas_connected_data' ] );
908
909 // Create Webhook Request Log table.
910 WebhookRequestsController::suretriggers_webhook_request_log_table();
911 // Schedule the cron jon to retry failed triggers.
912 WebhookRequestsController::suretriggers_setup_custom_cron();
913
914 $this->include_all_files( SURE_TRIGGERS_DIR . 'src/Integrations/' );
915 }
916
917 /**
918 * Added option to reset plugin in case of testing.
919 *
920 * @return void
921 */
922 public function reset_plugin() {
923 $nonce = sanitize_text_field( wp_unslash( isset( $_GET['_wpnonce'] ) ? $_GET['_wpnonce'] : false ) );
924
925 if ( $nonce && wp_verify_nonce( $nonce, 'st-reset-action' ) ) {
926 $is_reset = sanitize_text_field( wp_unslash( isset( $_GET['st-reset'] ) ? $_GET['st-reset'] : false ) );
927 if ( $is_reset && current_user_can( 'manage_options' ) ) {
928 delete_option( 'suretrigger_options' );
929 self::clear_verification_cache();
930 wp_safe_redirect( admin_url( 'admin.php?page=suretriggers' ) );
931 exit();
932 }
933 }
934 }
935
936 /**
937 * Custom Filter data.
938 *
939 * @param string $site_url Optional. Site URL to include in the iframe data.
940 * @return string
941 */
942 public function suretriggers_iframe_data( $site_url = '' ) {
943 $site_url = esc_url_raw( $site_url );
944 if ( ! current_user_can( 'manage_options' ) ) {
945 return $site_url;
946 }
947 $site_content_data = [
948 'stSaasURL' => $site_url . 'wp-login',
949 'stCode' => SaasApiToken::get(),
950 'baseUrl' => str_replace( '/wp-json/', '', get_rest_url() ),
951 'resetUrl' => rtrim( base64_encode( wp_nonce_url( admin_url( 'admin.php?st-reset=true' ), 'st-reset-action' ) ), '=' ), // phpcs:ignore
952 ];
953 $params = [
954 'st-code' => $site_content_data['stCode'],
955 'base_url' => $site_content_data['baseUrl'],
956 'reset_url' => $site_content_data['resetUrl'],
957 'redirect_url' => $site_url . 'embed-login',
958 'is_embedded' => true,
959 ];
960
961 if ( filter_var( $site_url, FILTER_VALIDATE_URL ) ) {
962 $iframe_url = add_query_arg( $params, $site_content_data['stSaasURL'] );
963 } else {
964 $default_url = trailingslashit( SURE_TRIGGERS_SITE_URL ) . '?path=dashboard';
965 $iframe_url = add_query_arg( $params, $default_url );
966 }
967 return esc_url_raw( $iframe_url );
968 }
969
970
971 /**
972 * Custom Filter data to check if user is logged in iframe.
973 *
974 * @return bool
975 */
976 public function suretriggers_saas_connected_data() {
977 if ( ! current_user_can( 'manage_options' ) ) {
978 return false;
979 }
980 $token = SaasApiToken::get();
981
982 if ( '' === $token || null === $token || false === $token || 'connection-denied' === $token ) {
983 $logged_in = false;
984 } else {
985 $logged_in = true;
986 }
987 return $logged_in;
988 }
989
990 /**
991 * Check if user has lifetime plan
992 *
993 * @return bool
994 */
995 public function is_lifetime_plan() {
996 // Check if verification data is already stored in database.
997 $verification_data = get_option( 'suretriggers_lifetime_user_plan_data' );
998 // Return lifetime plan status from cached data.
999 if ( is_array( $verification_data ) && isset( $verification_data['is_lifetime_plan'] ) ) {
1000 return (bool) $verification_data['is_lifetime_plan'];
1001 }
1002
1003 return false;
1004 }
1005
1006 /**
1007 * Check if user should see upgrade button (free or pro plan)
1008 *
1009 * @return bool
1010 */
1011 public function should_show_upgrade_button() {
1012 $verification_data = get_option( 'suretriggers_lifetime_user_plan_data' );
1013
1014 if ( is_array( $verification_data ) && isset( $verification_data['plan_id'] ) ) {
1015 $plan_id = $verification_data['plan_id'];
1016 return in_array( $plan_id, [ 'free', 'pro' ], true );
1017 }
1018
1019 return false;
1020 }
1021
1022 /**
1023 * Clear cached user verification data when token changes
1024 *
1025 * @return void
1026 */
1027 public static function clear_verification_cache() {
1028 delete_option( 'suretriggers_lifetime_user_plan_data' );
1029 }
1030 }
1031