PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / 3.3.9
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent v3.3.9
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 / models / class-wpsc-custom-field.php

class-wpsc-custom-field.php in SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent 3.3.9, at includes/models/class-wpsc-custom-field.php

755 lines 20.0 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_Custom_Field' ) ) :
7
8 final class WPSC_Custom_Field {
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 * Custom field type
40 *
41 * @var array
42 */
43 public static $cf_types;
44
45 /**
46 * Custom fields with slug indexing.
47 *
48 * @var array
49 */
50 public static $custom_fields = array();
51
52 /**
53 * DB object caching
54 *
55 * @var array
56 */
57 private static $cache = array();
58
59 /**
60 * Initialize this class
61 *
62 * @return void
63 */
64 public static function init() {
65
66 // Apply schema for this model.
67 add_action( 'init', array( __CLASS__, 'apply_schema' ), 2 );
68
69 // Get object of this class.
70 add_filter( 'wpsc_load_ref_classes', array( __CLASS__, 'load_ref_class' ) );
71
72 // Set custom field types.
73 add_action( 'init', array( __CLASS__, 'set_cf_types' ) );
74 }
75
76 /**
77 * Apply schema for this model
78 *
79 * @return void
80 */
81 public static function apply_schema() {
82
83 $schema = array(
84 'id' => array(
85 'has_ref' => false,
86 'ref_class' => '',
87 'has_multiple_val' => false,
88 ),
89 'name' => array(
90 'has_ref' => false,
91 'ref_class' => '',
92 'has_multiple_val' => false,
93 ),
94 'extra_info' => array(
95 'has_ref' => false,
96 'ref_class' => '',
97 'has_multiple_val' => false,
98 ),
99 'slug' => array(
100 'has_ref' => false,
101 'ref_class' => '',
102 'has_multiple_val' => false,
103 ),
104 'field' => array( // Set whether this field is ticket field, customer field or agentonly field.
105 'has_ref' => false,
106 'ref_class' => '',
107 'has_multiple_val' => false,
108 ),
109 'type' => array( // Set custom field type.. e.g. TextFeld, Dropdown, or any default types.
110 'has_ref' => true,
111 'ref_class' => 'wpsc_cft',
112 'has_multiple_val' => false,
113 ),
114 'default_value' => array( // If set, this value will be used if not provided for ticket or customer field types.
115 'has_ref' => false,
116 'ref_class' => '',
117 'has_multiple_val' => true,
118 ),
119 'placeholder_text' => array( // Placeholder on create ticket or edit ticket, customer or agentonly fields where applicable.
120 'has_ref' => false,
121 'ref_class' => '',
122 'has_multiple_val' => false,
123 ),
124 'char_limit' => array( // Number of charaters to accept where applicable.
125 'has_ref' => false,
126 'ref_class' => '',
127 'has_multiple_val' => false,
128 ),
129 'date_display_as' => array( // Used in Date Created, Date Updated and Date Closed to define whether you want to display date string or date difference on ticket list.
130 'has_ref' => false,
131 'ref_class' => '',
132 'has_multiple_val' => false,
133 ),
134 'date_range' => array( // Used in "Date" custom field whether to accept past, future or any dates.
135 'has_ref' => false,
136 'ref_class' => '',
137 'has_multiple_val' => false,
138 ),
139 'date_format' => array( // Used in "Date" custom field to display date in given format.
140 'has_ref' => false,
141 'ref_class' => '',
142 'has_multiple_val' => false,
143 ),
144 'start_range' => array( // Used in "Date Datetime and Time" custom field for start date or start time.
145 'has_ref' => true,
146 'ref_class' => 'datetime',
147 'has_multiple_val' => false,
148 ),
149 'end_range' => array( // Used in "Date Datetime and Time" custom field for end date or end time.
150 'has_ref' => true,
151 'ref_class' => 'datetime',
152 'has_multiple_val' => false,
153 ),
154 'time_format' => array( // Used in "Time" custom field to display time in 12 or 24 hrs.
155 'has_ref' => false,
156 'ref_class' => '',
157 'has_multiple_val' => false,
158 ),
159 'is_personal_info' => array( // Used for GDPR compatibility.
160 'has_ref' => false,
161 'ref_class' => '',
162 'has_multiple_val' => false,
163 ),
164 'load_order' => array(
165 'has_ref' => false,
166 'ref_class' => '',
167 'has_multiple_val' => false,
168 ),
169 'is_auto_fill' => array( // Used for fill value automatically.
170 'has_ref' => false,
171 'ref_class' => '',
172 'has_multiple_val' => false,
173 ),
174 'allow_ticket_form' => array( // Used for customer field are add or not in create ticket form.
175 'has_ref' => false,
176 'ref_class' => '',
177 'has_multiple_val' => false,
178 ),
179 'allow_my_profile' => array( // Used for customer field are show/hide in my profile.
180 'has_ref' => false,
181 'ref_class' => '',
182 'has_multiple_val' => false,
183 ),
184 'tl_width' => array( // Used in ticket list for column width.
185 'has_ref' => false,
186 'ref_class' => '',
187 'has_multiple_val' => false,
188 ),
189 'number_type' => array( // Used to set whether current field is integer or float.
190 'has_ref' => false,
191 'ref_class' => '',
192 'has_multiple_val' => false,
193 ),
194 );
195 self::$schema = apply_filters( 'wpsc_cf_schema', $schema );
196
197 // Prevent modify.
198 $prevent_modify = array( 'id', 'slug' );
199 self::$prevent_modify = apply_filters( 'wpsc_cf_prevent_modify', $prevent_modify );
200
201 // Set custom fields.
202 $custom_fields = self::find( array( 'items_per_page' => 0 ) )['results'];
203 foreach ( $custom_fields as $cf ) {
204 if ( ! class_exists( $cf->type ) ) {
205 continue;
206 }
207 self::$custom_fields[ $cf->slug ] = $cf;
208 self::$cache[ $cf->id ] = $cf;
209 }
210 }
211
212 /**
213 * Model constructor
214 *
215 * @param int $id - Optional. Data record id to retrive object for.
216 */
217 public function __construct( $id = 0 ) {
218
219 global $wpdb;
220
221 $id = intval( $id );
222
223 if ( $id && isset( self::$cache[ $id ] ) ) {
224 $this->data = self::$cache[ $id ]->data;
225 }
226
227 if ( $id > 0 ) {
228 $cf = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}psmsc_custom_fields WHERE id = " . $id, ARRAY_A );
229 if ( ! is_array( $cf ) ) {
230 return;
231 }
232
233 foreach ( $cf as $key => $val ) {
234
235 if ( $key == 'name' ) {
236
237 $this->data[ $key ] = $val !== null ? WPSC_Translations::get( 'wpsc-cf-name-' . $cf['id'], stripslashes( $val ) ) : '';
238
239 } elseif ( $key == 'extra_info' ) {
240
241 $this->data[ $key ] = $val !== null ? WPSC_Translations::get( 'wpsc-cf-exi-' . $cf['id'], stripslashes( $val ) ) : '';
242
243 } elseif ( $key == 'placeholder_text' ) {
244 $this->data[ $key ] = $val !== null ? WPSC_Translations::get( 'wpsc-cf-pl-txt-' . $cf['id'], stripslashes( $val ) ) : '';
245
246 } else {
247
248 $this->data[ $key ] = $val !== null ? $val : '';
249 }
250 }
251 }
252 }
253
254 /**
255 * Convert object into an array
256 *
257 * @return array
258 */
259 public function to_array() {
260
261 return $this->data;
262 }
263
264 /**
265 * Magic get function to use with object arrow function
266 *
267 * @param string $var_name - variable name.
268 * @return mixed
269 */
270 public function __get( $var_name ) {
271
272 if ( ! isset( $this->data[ $var_name ] ) ||
273 $this->data[ $var_name ] == null ||
274 $this->data[ $var_name ] == ''
275 ) {
276 return self::$schema[ $var_name ]['has_multiple_val'] ? array() : '';
277 }
278
279 if ( self::$schema[ $var_name ]['has_multiple_val'] ) {
280
281 $response = array();
282 $values = $this->data[ $var_name ] ? explode( '|', $this->data[ $var_name ] ) : array();
283 foreach ( $values as $val ) {
284 $response[] = self::$schema[ $var_name ]['has_ref'] ?
285 WPSC_Functions::get_object( self::$schema[ $var_name ]['ref_class'], $val ) :
286 $val;
287 }
288 return $response;
289
290 } else {
291
292 return self::$schema[ $var_name ]['has_ref'] && $this->data[ $var_name ] ?
293 WPSC_Functions::get_object( self::$schema[ $var_name ]['ref_class'], $this->data[ $var_name ] ) :
294 $this->data[ $var_name ];
295 }
296 }
297
298 /**
299 * Magic function to use setting object field with arrow function
300 *
301 * @param string $var_name - (Required) property slug.
302 * @param mixed $value - (Required) value to set for a property.
303 * @return void
304 */
305 public function __set( $var_name, $value ) {
306
307 if ( in_array( $var_name, self::$prevent_modify ) ) {
308 return;
309 }
310
311 $data_val = '';
312 if ( self::$schema[ $var_name ]['has_multiple_val'] ) {
313
314 $data_vals = array_map(
315 fn( $val ) => is_object( $val ) ? WPSC_Functions::set_object( self::$schema[ $var_name ]['ref_class'], $val ) : $val,
316 $value
317 );
318
319 $data_val = $data_vals ? implode( '|', $data_vals ) : '';
320
321 } else {
322
323 $data_val = is_object( $value ) ? WPSC_Functions::set_object( self::$schema[ $var_name ]['ref_class'], $value ) : $value;
324 }
325
326 if ( isset( $this->data[ $var_name ] ) && $this->data[ $var_name ] == $data_val ) {
327 return;
328 }
329
330 $this->data[ $var_name ] = $data_val;
331 $this->is_modified = true;
332 }
333
334 /**
335 * Save changes made
336 *
337 * @return boolean
338 */
339 public function save() {
340
341 global $wpdb;
342
343 if ( ! $this->is_modified ) {
344 return true;
345 }
346
347 $data = $this->data;
348 $success = true;
349
350 if ( ! isset( $data['id'] ) ) {
351
352 $cf = self::insert( $data );
353 if ( $cf ) {
354 $this->data = $cf->data;
355 $success = true;
356 } else {
357 $success = false;
358 }
359 } else {
360
361 unset( $data['id'] );
362 $success = $wpdb->update(
363 $wpdb->prefix . 'psmsc_custom_fields',
364 $data,
365 array( 'id' => $this->id )
366 );
367
368 WPSC_Translations::add( 'wpsc-cf-name-' . $this->id, $data['name'] );
369 $data['extra_info'] ? WPSC_Translations::add( 'wpsc-cf-exi-' . $this->id, $data['extra_info'] ) : WPSC_Translations::remove( 'wpsc-cf-exi-' . $this->id );
370 $data['placeholder_text'] ? WPSC_Translations::add( 'wpsc-cf-pl-txt-' . $this->id, $data['placeholder_text'] ) : WPSC_Translations::remove( 'wpsc-cf-pl-txt-' . $this->id );
371
372 if ( $success ) {
373 self::$custom_fields[ $this->slug ] = $this;
374 self::$cache[ $this->id ] = $this;
375 }
376 }
377 $this->is_modified = false;
378 return $success ? true : false;
379 }
380
381 /**
382 * Insert new record
383 *
384 * @param array $data - insert data.
385 * @return WPSC_Custom_Field
386 */
387 public static function insert( $data ) {
388
389 global $wpdb;
390
391 if ( ! isset( $data['load_order'] ) ) {
392 $max_order = $wpdb->get_var( "SELECT MAX(load_order) FROM {$wpdb->prefix}psmsc_custom_fields" );
393 $data['load_order'] = intval( $max_order ) + 1;
394 }
395
396 $success = $wpdb->insert(
397 $wpdb->prefix . 'psmsc_custom_fields',
398 $data
399 );
400
401 if ( ! $success ) {
402 return false;
403 }
404
405 $id = $wpdb->insert_id;
406
407 // string translation.
408 WPSC_Translations::add( 'wpsc-cf-name-' . $id, $data['name'] );
409 if ( isset( $data['extra_info'] ) ) {
410 WPSC_Translations::add( 'wpsc-cf-exi-' . $id, $data['extra_info'] );
411 }
412 if ( isset( $data['placeholder_text'] ) ) {
413 WPSC_Translations::add( 'wpsc-cf-pl-txt-' . $id, $data['placeholder_text'] );
414 }
415
416 $cf = new WPSC_Custom_Field( $id );
417
418 // Save slug if not already set. Reasone we have to do it here because slug is in $prevent_modify.
419 if ( ! $cf->slug ) {
420 $slug = 'cust_' . $cf->id;
421 $wpdb->update(
422 $wpdb->prefix . 'psmsc_custom_fields',
423 array( 'slug' => $slug ),
424 array( 'id' => $cf->id )
425 );
426 $cf->data['slug'] = $slug;
427 }
428
429 // Check if user has privileges to change innodb_strict_mode.
430 $has_privileges = $wpdb->get_var( 'SELECT @@innodb_strict_mode' ) !== null;
431 if ( $has_privileges ) {
432 // Disable InnoDB strict mode if enabled to avoid limit of number of columns in tables.
433 $strict_mode = $wpdb->get_var( "SHOW VARIABLES LIKE 'innodb_strict_mode'", 1 );
434 if ( $strict_mode == 'ON' ) {
435 $wpdb->query( "SET innodb_strict_mode = 'OFF'" );
436 }
437 }
438
439 // Add column to tickets table if field is either ticket or agentonly.
440 if ( in_array( $cf->field, array( 'ticket', 'agentonly' ) ) && $cf->type::$slug != 'cf_html' ) {
441 $success = $cf->create_ticket_tbl_col();
442 }
443
444 // Add column to customer table if field is customer.
445 if ( $cf->field === 'customer' ) {
446 $success = $cf->create_customer_tbl_col();
447 }
448
449 $success = apply_filters( 'wpsc_add_cf_table_column', $success, $cf );
450
451 self::$custom_fields[ $cf->slug ] = $cf;
452 self::$cache[ $cf->id ] = $cf;
453
454 if ( ! $success ) {
455 return false;
456 }
457 return $cf;
458 }
459
460 /**
461 * Delete record of given ID
462 *
463 * @param int $id - category id.
464 * @return boolean
465 */
466 public static function destroy( $id ) {
467
468 global $wpdb;
469
470 $cf = new WPSC_Custom_Field( $id );
471 if ( $cf->type::$is_default ) {
472 return false;
473 }
474
475 if ( in_array( $cf->field, array( 'ticket', 'agentonly' ) ) && $cf->type::$slug != 'cf_html' ) {
476 $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_tickets DROP {$cf->slug}" );
477 }
478
479 if ( $cf->field === 'customer' ) {
480 $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_customers DROP {$cf->slug}" );
481 }
482
483 do_action( 'wpsc_custom_field_before_destroy', $cf );
484
485 // Remove from DB.
486 $success = $wpdb->delete(
487 $wpdb->prefix . 'psmsc_custom_fields',
488 array( 'id' => $cf->id )
489 );
490 if ( ! $success ) {
491 return false;
492 }
493
494 // Remove options if any.
495 if ( $cf->type::$has_options ) {
496 $cf->delete_options();
497 }
498
499 unset( self::$custom_fields[ $cf->slug ] );
500 unset( self::$cache[ $cf->id ] );
501
502 // remove string translations.
503 WPSC_Translations::remove( 'wpsc-cf-name-' . $cf->id );
504 WPSC_Translations::remove( 'wpsc-cf-exi-' . $cf->id );
505 WPSC_Translations::remove( 'wpsc-cf-pl-txt-' . $cf->id );
506
507 return true;
508 }
509
510 /**
511 * Set data to create new object using direct data. Used in find method
512 *
513 * @param array $data - data to set for object.
514 * @return void
515 */
516 private function set_data( $data ) {
517
518 foreach ( $data as $var_name => $val ) {
519
520 if ( $var_name == 'name' ) {
521
522 $this->data[ $var_name ] = $val !== null ? WPSC_Translations::get( 'wpsc-cf-name-' . $data['id'], stripslashes( $val ) ) : '';
523
524 } elseif ( $var_name == 'extra_info' ) {
525
526 $this->data[ $var_name ] = $val !== null ? WPSC_Translations::get( 'wpsc-cf-exi-' . $data['id'], stripslashes( $val ) ) : '';
527
528 } elseif ( $var_name == 'placeholder_text' ) {
529
530 $this->data[ $var_name ] = $val !== null ? WPSC_Translations::get( 'wpsc-cf-pl-txt-' . $data['id'], stripslashes( $val ) ) : '';
531
532 } else {
533
534 $this->data[ $var_name ] = $val !== null ? $val : '';
535 }
536 }
537 }
538
539 /**
540 * Find records based on given filters
541 *
542 * @param array $filter - array containing array items like search, where, orderby, order, page_no, items_per_page, etc.
543 * @param boolean $is_object - return data as array or object. Default object.
544 * @return mixed
545 */
546 public static function find( $filter = array(), $is_object = true ) {
547
548 global $wpdb;
549
550 $sql = 'SELECT * FROM ' . $wpdb->prefix . 'psmsc_custom_fields ';
551 $where = self::get_where( $filter );
552
553 $filter['items_per_page'] = isset( $filter['items_per_page'] ) ? $filter['items_per_page'] : 0;
554 $filter['page_no'] = isset( $filter['page_no'] ) ? $filter['page_no'] : 1;
555 $filter['orderby'] = isset( $filter['orderby'] ) ? $filter['orderby'] : 'load_order';
556 $filter['order'] = isset( $filter['order'] ) ? $filter['order'] : 'ASC';
557
558 $order = WPSC_Functions::parse_order( $filter );
559 $limit = WPSC_Functions::parse_limit( $filter );
560 $sql = $sql . $where . $order . $limit;
561 $results = $wpdb->get_results( $sql, ARRAY_A );
562
563 // total results.
564 $sql = 'SELECT count(id) FROM ' . $wpdb->prefix . 'psmsc_custom_fields ';
565 $total_items = $wpdb->get_var( $sql . $where );
566
567 $response = WPSC_Functions::parse_response( $results, $total_items, $filter );
568
569 // Return array.
570 if ( ! $is_object ) {
571 return $response;
572 }
573
574 // create and return array of objects.
575 $temp_results = array();
576 foreach ( $response['results'] as $cf ) {
577
578 $ob = new WPSC_Custom_Field();
579 $data = array();
580 foreach ( $cf as $key => $val ) {
581 $data[ $key ] = $val;
582 }
583 $ob->set_data( $data );
584 $temp_results[] = $ob;
585 }
586 $response['results'] = $temp_results;
587
588 return $response;
589 }
590
591 /**
592 * Get where for find method
593 *
594 * @param array $filter - user filter.
595 * @return array
596 */
597 private static function get_where( $filter ) {
598
599 $where = '';
600
601 // Set user defined filters.
602 $meta_query = isset( $filter['meta_query'] ) && $filter['meta_query'] ? $filter['meta_query'] : array();
603 $meta_query = apply_filters( 'wpsc_cf_meta_query', $meta_query, $filter );
604 if ( $meta_query ) {
605
606 $meta_query = WPSC_Functions::parse_user_filters( __CLASS__, $meta_query );
607 $where = $meta_query . ' ';
608 }
609
610 return $where ? 'WHERE ' . $where : '';
611 }
612
613 /**
614 * Load current class to reference classes
615 *
616 * @param array $classes - Associative array of class names indexed by its slug.
617 * @return array
618 */
619 public static function load_ref_class( $classes ) {
620
621 $classes['wpsc_cf'] = array(
622 'class' => __CLASS__,
623 'save-key' => 'id',
624 );
625 return $classes;
626 }
627
628 /**
629 * Create column for custom field in ticket table
630 *
631 * @return boolean
632 */
633 public function create_ticket_tbl_col() {
634
635 global $wpdb;
636 return $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_tickets ADD {$this->slug} {$this->type::$data_type}" );
637 }
638
639 /**
640 * Create column for custom field in customer table
641 *
642 * @return boolean
643 */
644 public function create_customer_tbl_col() {
645
646 global $wpdb;
647 return $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_customers ADD {$this->slug} {$this->type::$data_type}" );
648 }
649
650 /**
651 * Set custom field types
652 */
653 public static function set_cf_types() {
654
655 self::$cf_types = apply_filters( 'wpsc_cf_types', array() );
656 }
657
658 /**
659 * Reset options for the field
660 *
661 * @param array $options - option ids.
662 * @param boolean $is_edit - set whether it is called from edit custom field.
663 * @return void
664 */
665 public function set_options( $options, $is_edit = false ) {
666
667 global $wpdb;
668
669 if ( $is_edit ) {
670 $wpdb->update(
671 $wpdb->prefix . 'psmsc_options',
672 array( 'custom_field' => 0 ),
673 array( 'custom_field' => $this->id )
674 );
675 }
676
677 if ( $this->type::$has_options ) {
678 $count = 1;
679 foreach ( $options as $option_id ) {
680 $option = new WPSC_Option( $option_id );
681 if ( ! $option->id ) {
682 continue;
683 }
684 $option->custom_field = $this->id;
685 $option->load_order = $count++;
686 $option->save();
687 }
688 }
689 }
690
691 /**
692 * Get options for this field
693 */
694 public function get_options() {
695
696 $filter = array(
697 'meta_query' => array(
698 'relation' => 'AND',
699 array(
700 'slug' => 'custom_field',
701 'compare' => '=',
702 'val' => $this->id,
703 ),
704 ),
705 );
706 return WPSC_Option::find( $filter )['results'];
707 }
708
709 /**
710 * Delete all options for this custom field
711 */
712 public function delete_options() {
713
714 global $wpdb;
715 $wpdb->delete(
716 $wpdb->prefix . 'psmsc_options',
717 array( 'custom_field' => $this->id )
718 );
719 }
720
721 /**
722 * Return CF object by slug
723 *
724 * @param string $slug - custom field slug.
725 * @return WPSC_Custom_Field
726 */
727 public static function get_cf_by_slug( $slug ) {
728
729 $cf = isset( self::$custom_fields[ $slug ] ) ? self::$custom_fields[ $slug ] : false;
730 return $cf;
731 }
732
733 /**
734 * Set load order for ids given
735 *
736 * @param array $ids - custom field ids.
737 * @return void
738 */
739 public static function set_load_order( $ids ) {
740
741 $count = 1;
742 foreach ( $ids as $id ) {
743 $cf = new WPSC_Custom_Field( $id );
744 if ( ! $cf ) {
745 wp_send_json_error( __( 'Bad request!', 'supportcandy' ), 400 );
746 }
747 $cf->load_order = $count++;
748 $cf->save();
749 }
750 }
751 }
752 endif;
753
754 WPSC_Custom_Field::init();
755