PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / 3.5.2
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent v3.5.2
3.5.4 3.5.3 3.5.2 3.5.1 3.4.9 3.5.0 3.4.8 3.4.7 trunk 2.3.1 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6
supportcandy / includes / admin / tickets / widgets / class-wpsc-itw-agent-collision.php

class-wpsc-itw-agent-collision.php in SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent 3.5.2, at includes/admin/tickets/widgets/class-wpsc-itw-agent-collision.php

352 lines 10.8 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 if ( ! class_exists( 'WPSC_ITW_Agent_Collision' ) ) :
8
9 final class WPSC_ITW_Agent_Collision {
10
11 /**
12 * Initialization
13 *
14 * @return void
15 */
16 public static function init() {
17
18 // agent collision.
19 add_action( 'wp_ajax_wpsc_check_live_agents', array( __CLASS__, 'check_live_agents' ) );
20 add_action( 'wp_ajax_nopriv_wpsc_check_live_agents', array( __CLASS__, 'check_live_agents' ) );
21
22 // edit widget settings.
23 add_action( 'wp_ajax_wpsc_get_tw_agent_collision', array( __CLASS__, 'get_tw_agent_collision' ) );
24 add_action( 'wp_ajax_wpsc_set_tw_agent_collision', array( __CLASS__, 'set_tw_agent_collision' ) );
25
26 add_action( 'wpsc_it_layout_section', array( __CLASS__, 'print_agent_collision_scripts' ) );
27 }
28
29 /**
30 * Print body of current widget
31 *
32 * @param WPSC_Ticket $ticket - ticket object.
33 * @param array $settings - setting array.
34 * @return void
35 */
36 public static function print_widget( $ticket, $settings ) {
37
38 $current_user = WPSC_Current_User::$current_user;
39 $ms_advanced = get_option( 'wpsc-ms-advanced-settings' );
40
41 if (
42 ! $current_user->is_agent ||
43 ! $ms_advanced['agent-collision'] ||
44 WPSC_Individual_Ticket::$view_profile !== 'agent'
45 ) {
46 return;
47 }
48
49 WPSC_Individual_Ticket::$ticket = $ticket;
50 if ( WPSC_Individual_Ticket::$is_restricted ) {
51 return;
52 }
53
54 $title = $settings['title']
55 ? WPSC_Translations::get( 'wpsc-twt-agent-collision', stripslashes( $settings['title'] ) )
56 : stripslashes( $settings['title'] );
57 $no_agent_html = '<div class="wpsc-no-agents">No live agent found</div>';
58 ?>
59 <div class="wpsc-it-widget wpsc-itw-ac">
60 <div class="wpsc-widget-header">
61 <h2><?php echo esc_html( $title ); ?></h2>
62 <span class="wpsc-itw-toggle" data-widget="wpsc-itw-ac"><?php WPSC_Icons::get( 'chevron-up' ); ?></span>
63 </div>
64 <div class="wpsc-widget-body" style="max-height: 100px;">
65 <div class="wpsc-widget-default">
66 <div class="wpsc-agent-collision">
67 <div class="wpsc-live-agents"></div>
68 </div>
69 </div>
70 </div>
71 </div>
72 <?php
73 }
74
75 /**
76 * Print necessary scripts for agent collision widget
77 *
78 * @param WPSC_Ticket $ticket - ticket object.
79 * @return void
80 */
81 public static function print_agent_collision_scripts( $ticket ) {
82 $current_user = WPSC_Current_User::$current_user;
83 if ( ! $current_user->is_agent ) {
84 return;
85 }
86 ?>
87 <script>
88 supportcandy.agent_collision = true;
89 wpsc_get_live_agents(<?php echo intval( $current_user->agent->id ); ?>, <?php echo intval( $ticket->id ); ?>);
90 function wpsc_get_live_agents(agent_id, ticket_id){
91
92 // ALWAYS re-select DOM (important)
93 let container = jQuery('.wpsc-live-agents');
94 let parent = jQuery('.wpsc-agent-collision');
95
96 var current_tid = jQuery('#wpsc-current-ticket').val();
97 if (current_tid != ticket_id){
98 schedule_next(agent_id, ticket_id);
99 return;
100 }
101
102 const urlParams = new URLSearchParams(window.location.search);
103 let section = supportcandy.is_frontend === '0'
104 ? urlParams.get('section')
105 : urlParams.get('wpsc-section');
106
107 if (!(section == 'ticket-list' && (urlParams.has('id') || urlParams.has('ticket-id')))){
108 schedule_next(agent_id, ticket_id);
109 return;
110 }
111
112 var data = {
113 action: 'wpsc_check_live_agents',
114 agent_id,
115 ticket_id,
116 operation: 'check',
117 _ajax_nonce: supportcandy.nonce
118 };
119
120 jQuery.post(supportcandy.ajax_url, data, function (response) {
121 if (response && response.agents){
122 if (container.html() !== response.agents){
123 container.html(response.agents);
124 }
125 parent.stop(true, true).fadeIn(200);
126 } else {
127
128 let noAgentHTML = '<div class="wpsc-no-agents"><?php echo esc_attr__( 'No live agent found', 'supportcandy' ); ?></div>';
129 if (container.html() !== noAgentHTML){
130 container.html(noAgentHTML);
131 }
132 parent.stop(true, true).fadeIn(200);
133 }
134 });
135 schedule_next(agent_id, ticket_id);
136 }
137
138 // Keep polling simple & reliable
139 function schedule_next(agent_id, ticket_id){
140 setTimeout(function(){
141 wpsc_get_live_agents(agent_id, ticket_id);
142 }, 60000);
143 }
144
145 jQuery(document).ready(function(){
146 window.addEventListener('beforeunload', function () {
147 const urlParams = new URLSearchParams(window.location.search);
148
149 // Determine section
150 let section = supportcandy.is_frontend === '0'
151 ? urlParams.get('section')
152 : urlParams.get('wpsc-section');
153
154 // Only trigger if we are on the ticket detail page
155 if (!(section === 'ticket-list' && (urlParams.has('id') || urlParams.has('ticket-id')))) {
156 return;
157 }
158
159 // Prepare data
160 const data = new FormData();
161 data.append('action', 'wpsc_check_live_agents');
162 data.append('agent_id', '<?php echo intval( $current_user->agent->id ); ?>');
163 data.append('ticket_id', '<?php echo intval( $ticket->id ); ?>');
164 data.append('operation', 'leave');
165 data.append('_ajax_nonce', supportcandy.nonce);
166
167 // Send reliably on tab close
168 navigator.sendBeacon(supportcandy.ajax_url, data);
169 });
170 });
171 </script>
172 <?php
173 }
174
175 /**
176 * Get list of live agents.
177 *
178 * @return void
179 */
180 public static function check_live_agents() {
181
182 if ( check_ajax_referer( 'general', '_ajax_nonce', false ) != 1 ) {
183 wp_send_json_error( 'Unauthorized request!', 401 );
184 }
185
186 $current_user = WPSC_Current_User::$current_user;
187 $ms_advanced = get_option( 'wpsc-ms-advanced-settings' );
188 if ( ! ( $current_user->is_agent && $ms_advanced['agent-collision'] ) ) {
189 wp_send_json_error( new WP_Error( '001', 'Unauthorized!' ), 401 );
190 }
191
192 $id = isset( $_POST['ticket_id'] ) ? intval( $_POST['ticket_id'] ) : 0; // phpcs:ignore
193 if ( ! $id ) {
194 wp_send_json_error( new WP_Error( '001', 'Unauthorized!' ), 401 );
195 }
196
197 // Always use the authenticated user's agent ID, not the request's agent_id.
198 if ( ! isset( $current_user->agent ) || ! $current_user->agent->id ) {
199 wp_send_json_error( new WP_Error( '001', 'Unauthorized!' ), 401 );
200 }
201
202 // If agent_id is present in the request, it must match the authenticated user's agent ID.
203 if ( isset( $_POST['agent_id'] ) && intval( $_POST['agent_id'] ) !== intval( $current_user->agent->id ) ) {
204 wp_send_json_error( new WP_Error( '004', 'Agent ID mismatch!' ), 401 );
205 }
206
207 $agent_id = intval( $current_user->agent->id );
208 $agent = new WPSC_Agent( $agent_id );
209 if ( ! $agent->id ) {
210 wp_send_json_error( new WP_Error( '002', 'Something went wrong!' ), 400 );
211 }
212
213 $ticket = new WPSC_Ticket( $id );
214 if ( ! $ticket->id ) {
215 wp_send_json_error( new WP_Error( '002', 'Something went wrong!' ), 400 );
216 }
217
218 WPSC_Individual_Ticket::$ticket = $ticket;
219 if ( ! WPSC_Individual_Ticket::has_ticket_cap( 'view' ) ) {
220 wp_send_json_error( new WP_Error( '003', 'Unauthorized!' ), 401 );
221 }
222
223 $operation = isset( $_POST['operation'] ) ? esc_attr( $_POST['operation'] ) : 'check'; // phpcs:ignore
224
225 $agents = json_decode( $ticket->live_agents, true );
226 $agents = $agents ? $agents : array();
227
228 if ( $operation == 'leave' ) {
229
230 if ( array_key_exists( $agent->id, $agents ) ) {
231 unset( $agents[ $agent->id ] );
232 $agents = wp_json_encode( $agents );
233 $ticket->live_agents = $agents;
234 $ticket->save();
235 }
236 wp_die();
237 }
238 // check inactive agents using timestamp comparison; remove if parsing fails or inactive > 60s.
239 foreach ( $agents as $key => $tm ) {
240 if ( $key == $agent->id ) {
241 continue;
242 }
243 $timestamp = strtotime( $tm );
244 if ( $timestamp === false || $timestamp < ( time() - 60 ) ) {
245 unset( $agents[ $key ] );
246 }
247 }
248
249 $agents[ $agent->id ] = ( new DateTime() )->format( 'Y-m-d H:i:s' );
250
251 $html = '';
252 foreach ( $agents as $ag_id => $tmp ) {
253 if ( $ag_id == $agent->id ) {
254 continue;
255 }
256 $agt = new WPSC_Agent( $ag_id );
257
258 $html .= '<div class="wpsc-agent-avatar">' .
259 get_avatar( $agt->customer->email, 28 ) .
260 '<span class="wpsc-agent-name">' . esc_html( $agt->name ) . '</span>' .
261 '</div>';
262 }
263
264 $agents = wp_json_encode( $agents );
265 $ticket->live_agents = $agents;
266 $ticket->save();
267 $response = array(
268 'agents' => $html,
269 );
270 wp_send_json( $response );
271 }
272
273 /**
274 * Add/edit Agent Collision title
275 *
276 * @return void
277 */
278 public static function get_tw_agent_collision() {
279
280 if ( ! WPSC_Functions::is_site_admin() ) {
281 wp_send_json_error( __( 'Unauthorized access!', 'supportcandy' ), 401 );
282 }
283
284 $ticket_widgets = get_option( 'wpsc-ticket-widget', array() );
285 $agent_collision = $ticket_widgets['agent-collision'];
286 $title = $agent_collision['title'];
287 ob_start();
288 ?>
289 <form action="#" onsubmit="return false;" class="wpsc-frm-edit-ac">
290 <div class="wpsc-input-group">
291 <div class="label-container">
292 <label for=""><?php echo esc_attr( wpsc__( 'Title', 'supportcandy' ) ); ?></label>
293 </div>
294 <input name="label" type="text" value="<?php echo esc_attr( $agent_collision['title'] ); ?>" autocomplete="off">
295 </div>
296 <input type="hidden" name="action" value="wpsc_set_tw_agent_collision">
297 <input type="hidden" name="_ajax_nonce" value="<?php echo esc_attr( wp_create_nonce( 'wpsc_set_tw_agent_collision' ) ); ?>">
298 </form>
299 <?php
300 $body = ob_get_clean();
301 ob_start();
302 ?>
303 <button class="wpsc-button small primary" onclick="wpsc_set_tw_agent_collision(this);">
304 <?php echo esc_attr( wpsc__( 'Submit', 'supportcandy' ) ); ?>
305 </button>
306 <button class="wpsc-button small secondary" onclick="wpsc_close_modal();">
307 <?php echo esc_attr( wpsc__( 'Cancel', 'supportcandy' ) ); ?>
308 </button>
309 <?php
310 $footer = ob_get_clean();
311 $response = array(
312 'title' => $title,
313 'body' => $body,
314 'footer' => $footer,
315 );
316 wp_send_json( $response );
317 }
318
319 /**
320 * Save Agent Collision label
321 *
322 * @return void
323 */
324 public static function set_tw_agent_collision() {
325
326 if ( check_ajax_referer( 'wpsc_set_tw_agent_collision', '_ajax_nonce', false ) != 1 ) {
327 wp_send_json_error( 'Unauthorized request!', 400 );
328 }
329
330 if ( ! WPSC_Functions::is_site_admin() ) {
331 wp_send_json_error( __( 'Unauthorized access!', 'supportcandy' ), 401 );
332 }
333
334 $label = isset( $_POST['label'] ) ? sanitize_text_field( wp_unslash( $_POST['label'] ) ) : '';
335 if ( ! $label ) {
336 wp_send_json_error( 'Bad Request', 400 );
337 }
338
339 $ticket_widgets = get_option( 'wpsc-ticket-widget', array() );
340 $ticket_widgets['agent-collision']['title'] = $label;
341 update_option( 'wpsc-ticket-widget', $ticket_widgets );
342
343 // remove string translations.
344 WPSC_Translations::remove( 'wpsc-twt-agent-collision' );
345 WPSC_Translations::add( 'wpsc-twt-agent-collision', stripslashes( $label ) );
346 wp_die();
347 }
348 }
349 endif;
350
351 WPSC_ITW_Agent_Collision::init();
352