PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.5.0
Forumax – AI Powered Advanced Community Forum Plugin v2.5.0
2.5.0 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 trunk 1.0.8 1.1.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 2.0.0 All 30 releases
bbp-core / includes / admin / class-support-assistant.php

class-support-assistant.php in Forumax – AI Powered Advanced Community Forum Plugin 2.5.0, at includes/admin/class-support-assistant.php

229 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit; // Exit if accessed directly.
5 }
6
7 /**
8 * Support Assistant
9 *
10 * Embeds the Spider Themes helpdesk docs assistant (EazyDocs cross-domain
11 * embed) on Forumax admin pages, scoped to the Forumax docs.
12 *
13 * Privacy:
14 * - The toggle styles, script and icons are bundled; only the iframe points
15 * to the helpdesk.
16 * - The iframe is loaded the first time the admin opens the chat, not on
17 * every page view.
18 * - The admin's email and name are passed (to pre-fill the helpdesk lead
19 * form) only when the site opted in to Freemius and the current user is
20 * the account that gave that consent. Otherwise the chat still works and
21 * the admin types their email into the lead form.
22 *
23 * Can be turned off in Forumax → Settings → General.
24 */
25 class Forumax_Support_Assistant {
26
27 const IFRAME_URL = 'https://helpdesk.spider-themes.net/iframe-assistant/';
28 const PRODUCT_CTX = 'Forumax';
29 const DOCS_SCOPE = 6420;
30
31 private static $instance;
32
33 /**
34 * Whether the embed should be printed on the current screen.
35 *
36 * @var bool
37 */
38 private $is_active = false;
39
40 /**
41 * Get class instance
42 *
43 * @return self
44 */
45 public static function get_instance(): self {
46 if ( null === self::$instance ) {
47 self::$instance = new self();
48 }
49 return self::$instance;
50 }
51
52 private function __construct() {
53 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
54 add_action( 'admin_footer', [ $this, 'render_embed' ] );
55 }
56
57 /**
58 * Whether the current admin screen belongs to Forumax.
59 *
60 * @return bool
61 */
62 private function is_plugin_screen(): bool {
63 $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
64
65 return 0 === strpos( $page, 'forumax' );
66 }
67
68 /**
69 * Whether "Hide Support Chat" is turned on in the settings.
70 *
71 * @return bool
72 */
73 private function is_hidden_in_settings(): bool {
74 return (bool) forumax_get_opt( 'hide_support_assistant', false );
75 }
76
77 /**
78 * Enqueue the bundled toggle styles and script on Forumax admin pages.
79 *
80 * @return void
81 */
82 public function enqueue_assets(): void {
83 if ( ! current_user_can( 'manage_options' ) || ! $this->is_plugin_screen() ) {
84 return;
85 }
86
87 /**
88 * Filter whether the helpdesk support assistant is shown on Forumax admin pages.
89 *
90 * @param bool $enabled False when "Hide Support Chat" is on in the settings.
91 */
92 if ( ! apply_filters( 'forumax_support_assistant_enabled', ! $this->is_hidden_in_settings() ) ) {
93 return;
94 }
95
96 $this->is_active = true;
97
98 wp_enqueue_style( 'forumax-support-assistant', FORUMAX_ASSETS . 'admin/css/support-assistant.css', [], FORUMAX_VERSION );
99 wp_enqueue_script( 'forumax-support-assistant', FORUMAX_ASSETS . 'admin/js/support-assistant.js', [], FORUMAX_VERSION, true );
100
101 // Spinner shown in the panel until the helpdesk page has loaded.
102 wp_add_inline_style(
103 'forumax-support-assistant',
104 '.forumax-support-assistant .chatbox-iframe-wraper.forumax-loading{background:#fff}
105 .forumax-support-assistant .chatbox-iframe-wraper.forumax-loading::after{content:"";position:absolute;top:50%;left:50%;width:32px;height:32px;margin:-16px 0 0 -16px;border:3px solid #e2e8f0;border-top-color:#0866ff;border-radius:50%;animation:forumax-support-spin .8s linear infinite}
106 @keyframes forumax-support-spin{to{transform:rotate(360deg)}}'
107 );
108
109 // Load the iframe on first open only. To make that feel instant:
110 // - hovering the button opens the connection to the helpdesk (DNS/TLS
111 // only, no data is sent), which saves ~1s on a cold connection;
112 // - loading starts on press rather than when the click completes.
113 wp_add_inline_script(
114 'forumax-support-assistant',
115 "(function () {
116 var root = document.querySelector('.forumax-support-assistant');
117 var toggle = root && root.querySelector('.chat-toggle');
118 var wrap = root && root.querySelector('.chatbox-iframe-wraper');
119 var iframe = root && root.querySelector('iframe[data-src]');
120 if (!toggle || !wrap || !iframe) return;
121 var src = iframe.getAttribute('data-src');
122 var warmed = false;
123 function warm() {
124 if (warmed) return;
125 warmed = true;
126 var link = document.createElement('link');
127 link.rel = 'preconnect';
128 link.href = new URL(src).origin;
129 document.head.appendChild(link);
130 }
131 function load() {
132 if (iframe.getAttribute('src')) return;
133 warm();
134 wrap.classList.add('forumax-loading');
135 iframe.addEventListener('load', function () { wrap.classList.remove('forumax-loading'); }, { once: true });
136 iframe.setAttribute('src', src);
137 }
138 toggle.addEventListener('pointerenter', warm);
139 toggle.addEventListener('pointerdown', load);
140 toggle.addEventListener('click', load);
141 })();"
142 );
143 }
144
145 /**
146 * The admin's email and name, only when Freemius consent covers them.
147 *
148 * Consent is given by the admin who opted in, so the current user must be
149 * that Freemius account; other admins on the same site get no pre-fill.
150 *
151 * @return array{email?: string, name?: string}
152 */
153 private function get_consented_lead(): array {
154 if ( ! function_exists( 'bc_fs' ) ) {
155 return [];
156 }
157
158 $fs = bc_fs();
159 if ( ! is_object( $fs ) || ! method_exists( $fs, 'is_tracking_allowed' ) || ! $fs->is_registered() || ! $fs->is_tracking_allowed() ) {
160 return [];
161 }
162
163 $fs_user = $fs->get_user();
164 $wp_user = wp_get_current_user();
165 if ( ! is_object( $fs_user ) || empty( $fs_user->email ) || 0 !== strcasecmp( $fs_user->email, $wp_user->user_email ) ) {
166 return [];
167 }
168
169 $name = trim( ( $fs_user->first ?? '' ) . ' ' . ( $fs_user->last ?? '' ) );
170
171 return [
172 'email' => sanitize_email( $fs_user->email ),
173 'name' => sanitize_text_field( '' !== $name ? $name : $wp_user->display_name ),
174 ];
175 }
176
177 /**
178 * Build the iframe URL: product scope, plus the admin's details when consented.
179 *
180 * @return string
181 */
182 private function get_iframe_url(): string {
183 $args = [
184 'ctx' => self::PRODUCT_CTX,
185 'ezd_scope' => self::DOCS_SCOPE,
186 ];
187
188 $lead = $this->get_consented_lead();
189 if ( ! empty( $lead['email'] ) ) {
190 $args['atml_email'] = $lead['email'];
191 $args['atml_name'] = $lead['name'];
192 }
193
194 /**
195 * Filter the query args passed to the helpdesk assistant iframe.
196 *
197 * @param array $args Query args (ctx, ezd_scope, and atml_email/atml_name when consented).
198 */
199 $args = (array) apply_filters( 'forumax_support_assistant_args', $args );
200 $args = array_filter( array_map( 'strval', $args ), 'strlen' );
201
202 return add_query_arg( array_map( 'rawurlencode', $args ), self::IFRAME_URL );
203 }
204
205 /**
206 * Print the embed markup in the admin footer.
207 *
208 * @return void
209 */
210 public function render_embed(): void {
211 if ( ! $this->is_active ) {
212 return;
213 }
214
215 ?>
216 <div class="eazydocs-cross-domain-code forumax-support-assistant">
217 <div class="chat-toggle">
218 <img class="wp-spotlight-chat" src="<?php echo esc_url( FORUMAX_ASSETS . 'admin/images/support-chat.svg' ); ?>" alt="<?php esc_attr_e( 'Chat Icon', 'forumax' ); ?>">
219 <img class="wp-spotlight-hide" src="<?php echo esc_url( FORUMAX_ASSETS . 'admin/images/support-close.svg' ); ?>" alt="<?php esc_attr_e( 'Close Icon', 'forumax' ); ?>" style="display: none;">
220 </div>
221 <button type="button" class="close-chat-sm"><span><?php esc_html_e( 'Hide', 'forumax' ); ?></span><span class="icon">❮</span></button>
222 <div class="chatbox-iframe-wraper">
223 <iframe data-src="<?php echo esc_url( $this->get_iframe_url() ); ?>" title="<?php esc_attr_e( 'Forumax Support Assistant', 'forumax' ); ?>" style="border: none;" frameborder="0"></iframe>
224 </div>
225 </div>
226 <?php
227 }
228 }
229