PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / trunk
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent vtrunk
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 / models / class-wpsc-working-hour.php

class-wpsc-working-hour.php in SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent trunk, at includes/models/class-wpsc-working-hour.php

709 lines 18.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly!
4 }
5
6 if ( ! class_exists( 'WPSC_Working_Hour' ) ) :
7
8 final class WPSC_Working_Hour {
9
10 /**
11 * Object data in key => val pair.
12 *
13 * @var array
14 */
15 private $data = array();
16
17 /**
18 * Set whether or not current object properties modified
19 *
20 * @var boolean
21 */
22 private $is_modified = false;
23
24 /**
25 * Schema for this model
26 *
27 * @var array
28 */
29 public static $schema = array();
30
31 /**
32 * Prevent fields to modify
33 *
34 * @var array
35 */
36 public static $prevent_modify = array();
37
38 /**
39 * DB object caching
40 *
41 * @var array
42 */
43 private static $cache = array();
44
45 /**
46 * Initialize this class
47 *
48 * @return void
49 */
50 public static function init() {
51
52 // Apply schema for this model.
53 add_action( 'init', array( __CLASS__, 'apply_schema' ), 2 );
54
55 // Get object of this class.
56 add_filter( 'wpsc_load_ref_classes', array( __CLASS__, 'load_ref_class' ) );
57
58 // Settings section.
59 add_action( 'wp_ajax_wpsc_get_working_hrs', array( __CLASS__, 'get_working_hrs' ) );
60 add_action( 'wp_ajax_wpsc_set_working_hrs', array( __CLASS__, 'set_working_hrs' ) );
61 }
62
63 /**
64 * Apply schema for this model
65 *
66 * @return void
67 */
68 public static function apply_schema() {
69
70 $schema = array(
71 'id' => array(
72 'has_ref' => false,
73 'ref_class' => '',
74 'has_multiple_val' => false,
75 ),
76 'agent' => array(
77 'has_ref' => true,
78 'ref_class' => 'wpsc_agent',
79 'has_multiple_val' => false,
80 ),
81 'day' => array(
82 'has_ref' => false,
83 'ref_class' => '',
84 'has_multiple_val' => false,
85 ),
86 'start_time' => array(
87 'has_ref' => false,
88 'ref_class' => '',
89 'has_multiple_val' => false,
90 ),
91 'end_time' => array(
92 'has_ref' => false,
93 'ref_class' => '',
94 'has_multiple_val' => false,
95 ),
96 );
97 self::$schema = apply_filters( 'wpsc_wh_schema', $schema );
98
99 // Prevent modify.
100 $prevent_modify = array( 'id' );
101 self::$prevent_modify = apply_filters( 'wpsc_wh_prevent_modify', $prevent_modify );
102 }
103
104 /**
105 * Model constructor
106 *
107 * @param int $id - Optional. Data record id to retrive object for.
108 */
109 public function __construct( $id = 0 ) {
110
111 global $wpdb;
112
113 $id = intval( $id );
114
115 if ( $id > 0 ) {
116
117 $working_hr = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}psmsc_working_hrs WHERE id = " . $id, ARRAY_A );
118 if ( ! is_array( $working_hr ) ) {
119 return;
120 }
121
122 foreach ( $working_hr as $key => $val ) {
123 $this->data[ $key ] = $val !== null ? $val : '';
124 }
125 }
126 }
127
128 /**
129 * Magic get function to use with object arrow function
130 *
131 * @param string $var_name - variable name.
132 * @return mixed
133 */
134 public function __get( $var_name ) {
135
136 if ( ! isset( $this->data[ $var_name ] ) ||
137 $this->data[ $var_name ] == null ||
138 $this->data[ $var_name ] == ''
139 ) {
140 return self::$schema[ $var_name ]['has_multiple_val'] ? array() : '';
141 }
142
143 return self::$schema[ $var_name ]['has_ref'] && $this->data[ $var_name ] ?
144 WPSC_Functions::get_object( self::$schema[ $var_name ]['ref_class'], $this->data[ $var_name ] ) :
145 $this->data[ $var_name ];
146 }
147
148 /**
149 * Magic function to use setting object field with arrow function
150 *
151 * @param string $var_name - (Required) property slug.
152 * @param mixed $value - (Required) value to set for a property.
153 * @return void
154 */
155 public function __set( $var_name, $value ) {
156
157 if (
158 ! isset( $this->data[ $var_name ] ) ||
159 in_array( $var_name, self::$prevent_modify )
160 ) {
161 return;
162 }
163
164 $data_val = is_object( $value ) ?
165 WPSC_Functions::set_object( self::$schema[ $var_name ]['ref_class'], $value ) :
166 $value;
167
168 if ( $this->data[ $var_name ] == $data_val ) {
169 return;
170 }
171
172 $this->data[ $var_name ] = $data_val;
173 $this->is_modified = true;
174 }
175
176 /**
177 * Save changes made
178 *
179 * @return boolean
180 */
181 public function save() {
182
183 global $wpdb;
184
185 if ( ! $this->is_modified ) {
186 return true;
187 }
188
189 $data = $this->data;
190
191 unset( $data['id'] );
192 $success = $wpdb->update(
193 $wpdb->prefix . 'psmsc_working_hrs',
194 $data,
195 array( 'id' => $this->data['id'] )
196 );
197
198 $this->is_modified = false;
199 return $success ? true : false;
200 }
201
202 /**
203 * Insert new record
204 *
205 * @param array $data - insert data.
206 * @return WPSC_Working_Hour
207 */
208 public static function insert( $data ) {
209
210 global $wpdb;
211
212 $success = $wpdb->insert(
213 $wpdb->prefix . 'psmsc_working_hrs',
214 $data
215 );
216
217 if ( ! $success ) {
218 return false;
219 }
220
221 $working_hr = new WPSC_Working_Hour( $wpdb->insert_id );
222 return $working_hr;
223 }
224
225 /**
226 * Set data to create new object using direct data. Used in find method
227 *
228 * @param array $data - data to set for object.
229 * @return void
230 */
231 private function set_data( $data ) {
232
233 foreach ( $data as $var_name => $val ) {
234 $this->data[ $var_name ] = $val !== null ? $val : '';
235 }
236 }
237
238 /**
239 * Find records based on given filters
240 *
241 * @param array $filter - array containing array items like search, where, orderby, order, page_no, items_per_page, etc.
242 * @param boolean $is_object - return data as array or object. Default object.
243 * @return mixed
244 */
245 public static function find( $filter = array(), $is_object = true ) {
246
247 global $wpdb;
248
249 $sql = 'SELECT * FROM ' . $wpdb->prefix . 'psmsc_working_hrs ';
250 $where = self::get_where( $filter );
251
252 $filter['items_per_page'] = isset( $filter['items_per_page'] ) ? $filter['items_per_page'] : 0;
253 $filter['page_no'] = isset( $filter['page_no'] ) ? $filter['page_no'] : 0;
254 $filter['orderby'] = isset( $filter['orderby'] ) ? $filter['orderby'] : 'day';
255 $filter['order'] = isset( $filter['order'] ) ? $filter['order'] : 'ASC';
256
257 $order = WPSC_Functions::parse_order( $filter );
258
259 $sql = $sql . $where . $order;
260 $results = $wpdb->get_results( $sql, ARRAY_A );
261
262 // total results.
263 $sql = 'SELECT count(id) FROM ' . $wpdb->prefix . 'psmsc_working_hrs ';
264 $total_items = $wpdb->get_var( $sql . $where );
265
266 $response = WPSC_Functions::parse_response( $results, $total_items, $filter );
267
268 // Return array.
269 if ( ! $is_object ) {
270 return $response;
271 }
272
273 // create and return array of objects.
274 $temp_results = array();
275 foreach ( $response['results'] as $working_hr ) {
276
277 $ob = new WPSC_Working_Hour();
278 $data = array();
279 foreach ( $working_hr as $key => $val ) {
280 $data[ $key ] = $val;
281 }
282 $ob->set_data( $data );
283 $temp_results[] = $ob;
284 }
285 $response['results'] = $temp_results;
286
287 return $response;
288 }
289
290 /**
291 * Get where for find method
292 *
293 * @param array $filter - user filter.
294 * @return array
295 */
296 private static function get_where( $filter ) {
297
298 $where = '';
299
300 // Set user defined filters.
301 $meta_query = isset( $filter['meta_query'] ) && $filter['meta_query'] ? $filter['meta_query'] : array();
302 if ( $meta_query ) {
303 $meta_query = WPSC_Functions::parse_user_filters( __CLASS__, $meta_query );
304 $where = $meta_query . ' ';
305 }
306
307 return $where ? 'WHERE ' . $where : '';
308 }
309
310 /**
311 * Get working hrs of agent
312 *
313 * @param int $agent_id - Agent ID.
314 * @return array
315 */
316 public static function get( $agent_id = 0 ) {
317
318 // return from cache if found.
319 if ( isset( self::$cache[ $agent_id ] ) ) {
320 return self::$cache[ $agent_id ];
321 }
322
323 // get it from db.
324 $working_hrs = self::find(
325 array(
326 'meta_query' => array(
327 'relation' => 'AND',
328 array(
329 'slug' => 'agent',
330 'compare' => '=',
331 'val' => $agent_id,
332 ),
333 ),
334 )
335 );
336 $response = array();
337 foreach ( $working_hrs['results'] as $key => $working_hr ) {
338 $response[ $key + 1 ] = $working_hr;
339 }
340
341 // add it to cache.
342 self::$cache[ $agent_id ] = $response;
343
344 return $response;
345 }
346
347 /**
348 * Set working hrs of given agent id
349 *
350 * @param array $wh - weekly working hrs.
351 * @param integer $agent_id - agent id.
352 * @return void
353 */
354 public static function set( $wh, $agent_id = 0 ) {
355
356 // sanitize request data.
357 $working_hrs = array();
358 foreach ( $wh as $day => $working_hr ) {
359
360 $day = intval( $day );
361 if ( ! $day ) {
362 wp_send_json_error( 'Bad request', 400 );
363 }
364
365 $start_time = isset( $working_hr['start_time'] ) ? sanitize_text_field( $working_hr['start_time'] ) : '';
366 if ( ! $start_time ) {
367 wp_send_json_error( 'Bad request', 400 );
368 }
369
370 $end_time = isset( $working_hr['end_time'] ) ? sanitize_text_field( $working_hr['end_time'] ) : '';
371 if ( ! $end_time ) {
372 wp_send_json_error( 'Bad request', 400 );
373 }
374
375 $working_hrs[ $day ] = array(
376 'start_time' => $start_time,
377 'end_time' => $end_time,
378 );
379 }
380
381 // save changes.
382 $whs = self::get( $agent_id );
383 for ( $i = 1; $i <= 7; $i++ ) {
384
385 $working_hr = $whs[ $i ];
386
387 $start_time = $working_hrs[ $i ]['start_time'];
388 $working_hr->start_time = $start_time;
389
390 $end_time = $start_time != 'off' ? $working_hrs[ $i ]['end_time'] : 'off';
391 $working_hr->end_time = $end_time;
392
393 $working_hr->save();
394 }
395
396 // remove from cache so that next time it will be pulled from db.
397 unset( self::$cache[ $agent_id ] );
398 }
399
400 /**
401 * Load current class to reference classes
402 *
403 * @param array $classes - Associative array of class names indexed by its slug.
404 * @return array
405 */
406 public static function load_ref_class( $classes ) {
407
408 $classes['wpsc_working_hr'] = array(
409 'class' => __CLASS__,
410 'save-key' => 'id',
411 );
412 return $classes;
413 }
414
415 /**
416 * Get working hrs settings
417 *
418 * @return void
419 */
420 public static function get_working_hrs() {
421
422 if ( ! WPSC_Functions::is_site_admin() ) {
423 wp_send_json_error( __( 'Unauthorized access!', 'supportcandy' ), 401 );
424 }
425
426 $working_hrs = self::get();?>
427
428 <form onsubmit="return false;" class="wpsc-wh-settings">
429 <div class="wpsc-dock-container">
430 <?php
431 printf(
432 /* translators: Click here to see the documentation */
433 esc_attr__( '%s to see the documentation!', 'supportcandy' ),
434 '<a href="https://supportcandy.net/docs/working-hours/" target="_blank">' . esc_attr__( 'Click here', 'supportcandy' ) . '</a>'
435 );
436 ?>
437 </div>
438 <table class="wpsc-working-hrs">
439 <?php
440 for ( $i = 1; $i <= 7; $i++ ) :
441 $start_time = $working_hrs[ $i ]->start_time;
442 $end_time = $working_hrs[ $i ]->end_time;
443 $style = $start_time == 'off' ? 'display: none;' : '';
444 ?>
445 <tr>
446 <td class="dayName"><?php echo esc_attr( WPSC_Functions::get_day_name( $i ) ); ?>:</td>
447 <td>
448 <select class="wpsc-wh-start-time" name="wh[<?php echo esc_attr( $i ); ?>][start_time]">
449 <?php self::get_start_time_slots( $start_time ); ?>
450 </select>
451 </td>
452 <td style="<?php echo esc_attr( $style ); ?>">-</td>
453 <td style="<?php echo esc_attr( $style ); ?>">
454 <select class="wpsc-wh-end-time" name="wh[<?php echo esc_attr( $i ); ?>][end_time]">
455 <?php self::get_end_time_slots( $start_time, $end_time ); ?>
456 </select>
457 </td>
458 </tr>
459 <?php
460 endfor;
461 ?>
462 </table>
463 <input type="hidden" name="action" value="wpsc_set_working_hrs">
464 <input type="hidden" name="_ajax_nonce" value="<?php echo esc_attr( wp_create_nonce( 'wpsc_set_working_hrs' ) ); ?>">
465 </form>
466 <div class="setting-footer-actions">
467 <button
468 class="wpsc-button normal primary margin-right"
469 onclick="wpsc_set_working_hrs(this);">
470 <?php esc_attr_e( 'Submit', 'supportcandy' ); ?>
471 </button>
472 </div>
473 <script>
474 var end_times = [];
475 <?php
476 $current_slot = new DateTime( '2020-01-01 00:15:00' );
477 $second_last_slot = new DateTime( '2020-01-01 23:45:00' );
478 $last_slot = new DateTime( '2020-01-01 23:59:59' );
479
480 do {
481 $time = $current_slot->format( 'H:i:s' )
482 ?>
483 end_times.push({
484 val: '<?php echo esc_attr( $time ); ?>',
485 display_val: '<?php echo esc_attr( $current_slot->format( 'H:i' ) ); ?>',
486 });
487 <?php
488 if ( $current_slot == $second_last_slot ) {
489 $current_slot->add( new DateInterval( 'PT14M59S' ) );
490 } else {
491 $current_slot->add( new DateInterval( 'PT15M' ) );
492 }
493 } while ( $current_slot <= $last_slot );
494 ?>
495 supportcandy.temp = {end_times};
496
497 // Change event
498 jQuery('.wpsc-wh-start-time').change(function(){
499 var start_time = jQuery(this).val();
500 var td1 = jQuery(this).parent().next();
501 var td2 = td1.next();
502 if (start_time === 'off') {
503 td1.hide();
504 td2.hide();
505 return;
506 } else {
507 td1.show();
508 td2.show();
509 }
510 var tempArr = start_time.split(":");
511 var startDate = new Date(2020, 0, 1, tempArr[0], tempArr[1], tempArr[2]);
512 var cmbEndTime = jQuery(this).closest('tr').find('.wpsc-wh-end-time');
513 cmbEndTime.find('option').remove();
514 jQuery.each(supportcandy.temp.end_times, function(index, end_time){
515 var tempArr = end_time.val.split(":");
516 var endDate = new Date(2020, 0, 1, tempArr[0], tempArr[1], tempArr[2]);
517 if (startDate < endDate) {
518 var obj = document.createElement('OPTION');
519 var displayVal = document.createTextNode(end_time.display_val);
520 obj.setAttribute("value", end_time.val);
521 obj.appendChild(displayVal);
522 cmbEndTime.append(obj);
523 }
524 });
525 });
526 </script>
527 <?php
528 wp_die();
529 }
530
531 /**
532 * Set company working hrs
533 *
534 * @return void
535 */
536 public static function set_working_hrs() {
537
538 if ( check_ajax_referer( 'wpsc_set_working_hrs', '_ajax_nonce', false ) != 1 ) {
539 wp_send_json_error( 'Unauthorized request!', 401 );
540 }
541
542 if ( ! WPSC_Functions::is_site_admin() ) {
543 wp_send_json_error( __( 'Unauthorized access!', 'supportcandy' ), 401 );
544 }
545
546 $wh = isset( $_POST['wh'] ) ? map_deep( wp_unslash( $_POST['wh'] ), 'sanitize_text_field' ) : array();
547 if ( ! $wh ) {
548 wp_send_json_error( 'Bad request', 400 );
549 }
550
551 self::set( $wh );
552 wp_die();
553 }
554
555 /**
556 * Get start time slots
557 *
558 * @param string $start_time - time slots from.
559 * @return void
560 */
561 public static function get_start_time_slots( $start_time ) {
562
563 $current_slot = new DateTime( '2020-01-01 00:00:00' );
564 $last_slot = new DateTime( '2020-01-01 23:45:00' );
565 ?>
566 <option value="off"><?php esc_attr_e( 'OFF', 'supportcandy' ); ?></option>
567 <?php
568 do {
569 $time = $current_slot->format( 'H:i:s' );
570 ?>
571 <option <?php selected( $time, $start_time ); ?> value="<?php echo esc_attr( $time ); ?>"><?php echo esc_attr( $current_slot->format( 'H:i' ) ); ?></option>
572 <?php
573 $current_slot->add( new DateInterval( 'PT15M' ) );
574 } while ( $current_slot <= $last_slot );
575 }
576
577 /**
578 * Get end time slots
579 *
580 * @param string $start_time - start time for reference. end time will be greater than start time.
581 * @param string $end_time - preselected end time.
582 * @return void
583 */
584 public static function get_end_time_slots( $start_time, $end_time ) {
585
586 $current_slot = new DateTime( '2020-01-01 00:15:00' );
587 $second_last_slot = new DateTime( '2020-01-01 23:45:00' );
588 $last_slot = new DateTime( '2020-01-01 23:59:59' );
589
590 do {
591 $time = $current_slot->format( 'H:i:s' );
592 ?>
593 <option <?php selected( $time, $end_time ); ?> value="<?php echo esc_attr( $time ); ?>"><?php echo esc_attr( $current_slot->format( 'H:i' ) ); ?></option>
594 <?php
595 if ( $current_slot == $second_last_slot ) {
596 $current_slot->add( new DateInterval( 'PT14M59S' ) );
597 } else {
598 $current_slot->add( new DateInterval( 'PT15M' ) );
599 }
600 } while ( $current_slot <= $last_slot );
601 }
602
603 /**
604 * Return working hrs for given date for company or agent
605 *
606 * @param DateTime $date - given date.
607 * @param integer $agent_id - agent id form whom working hrs to returned from.
608 * @return boolean
609 */
610 public static function get_working_hrs_by_date( $date, $agent_id = 0 ) {
611
612 $date = ( clone $date )->setTime( 0, 0 );
613
614 // check agent leave on this date. Not applicable for company.
615 if ( $agent_id ) {
616 $holiday = WPSC_Holiday::get_holiday_by_date( $date, $agent_id );
617 if ( $holiday ) {
618 return false;
619 }
620 }
621
622 // check for exception on this date.
623 $exception = WPSC_Wh_Exception::get_exception_by_date( $date, $agent_id );
624 if ( $exception ) {
625 return array(
626 'start_time' => $exception->start_time,
627 'end_time' => $exception->end_time,
628 );
629 }
630
631 // check comapny holiday.
632 if ( $agent_id == 0 ) {
633 $holiday = WPSC_Holiday::get_holiday_by_date( $date, $agent_id );
634 if ( $holiday ) {
635 return false;
636 }
637 }
638
639 // get working hrs for date.
640 $working_hrs = self::get( $agent_id );
641 $wh = $working_hrs[ $date->format( 'N' ) ];
642
643 // check whether it is off.
644 if ( $wh->start_time == 'off' ) {
645 return false;
646 }
647
648 // return working hrs.
649 return array(
650 'start_time' => $wh->start_time,
651 'end_time' => $wh->end_time,
652 );
653 }
654
655 /**
656 * Get closest working hr for company or an agent
657 *
658 * @param DateTime $date - date from which closest working hrs to be given.
659 * @param integer $agent_id - agent id for whom working hrs to be returned.
660 * @return array
661 */
662 public static function get_closest_wh_by_date( $date, $agent_id = 0 ) {
663
664 $tz = wp_timezone();
665 $date = clone $date;
666
667 // check for given date.
668 $wh = self::get_working_hrs_by_date( $date, $agent_id );
669 if ( $wh ) {
670
671 // calculate maximum start time for given date.
672 $max_start = new DateTime( $date->format( 'Y-m-d' ) . ' ' . $wh['end_time'], $tz );
673 if ( $wh['end_time'] == '23:59:59' ) {
674 $max_start->sub( new DateInterval( 'PT14M' ) );
675 } else {
676 $max_start->sub( new DateInterval( 'PT15M' ) );
677 }
678
679 // return working hr if given date is less than maxstart time.
680 if ( $date < $max_start ) {
681 $start_time = new DateTime( $date->format( 'Y-m-d' ) . ' ' . $wh['start_time'], $tz );
682 if ( $date > $start_time ) {
683 $start_time = $date;
684 }
685 return array(
686 'start_time' => $start_time,
687 'end_time' => new DateTime( $date->format( 'Y-m-d' ) . ' ' . $wh['end_time'], $tz ),
688 );
689 }
690 }
691
692 do {
693
694 $date->add( new DateInterval( 'P1D' ) );
695 $wh = self::get_working_hrs_by_date( $date, $agent_id );
696
697 if ( $wh ) {
698 return array(
699 'start_time' => new DateTime( $date->format( 'Y-m-d' ) . ' ' . $wh['start_time'], $tz ),
700 'end_time' => new DateTime( $date->format( 'Y-m-d' ) . ' ' . $wh['end_time'], $tz ),
701 );
702 }
703 } while ( true );
704 }
705 }
706 endif;
707
708 WPSC_Working_Hour::init();
709