PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / 3.5.4
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent v3.5.4
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
← All changes | class-wpsc-installation.php +561 -140 3.4.2 → 3.5.4 View file →
@@ -232,8 +232,9 @@
232 232 source VARCHAR(200) NOT NULL,
233 233 source_id BIGINT NOT NULL DEFAULT 0,
234 234 ticket_id BIGINT NOT NULL DEFAULT 0,
235 235 customer_id BIGINT NOT NULL DEFAULT 0,
236 + uploaded_by VARCHAR(64) NOT NULL DEFAULT '',
236 237 PRIMARY KEY (id)
237 238 ) $collate;
238 239 CREATE TABLE {$wpdb->prefix}psmsc_agents (
239 240 id BIGINT NOT NULL AUTO_INCREMENT,
@@ -322,8 +323,70 @@
322 323 color VARCHAR(50) NOT NULL,
323 324 bg_color VARCHAR(50) NOT NULL,
324 325 PRIMARY KEY (id)
325 326 ) $collate;
327 + CREATE TABLE {$wpdb->prefix}psmsc_ai_logs (
328 + id BIGINT NOT NULL AUTO_INCREMENT,
329 + customer BIGINT NOT NULL,
330 + ticket BIGINT NOT NULL,
331 + provider VARCHAR(100) NOT NULL,
332 + model VARCHAR(100) NOT NULL,
333 + feature VARCHAR(50) NOT NULL,
334 + tokens INT NOT NULL,
335 + prompt LONGTEXT NOT NULL,
336 + date_created DATETIME NOT NULL,
337 + PRIMARY KEY (id),
338 + KEY customer (customer),
339 + KEY date_created (date_created)
340 + ) $collate;
341 + CREATE TABLE {$wpdb->prefix}psmsc_ai_training (
342 + id BIGINT NOT NULL AUTO_INCREMENT,
343 + status VARCHAR(20) NOT NULL,
344 + provider VARCHAR(50) NOT NULL,
345 + source VARCHAR(50) NOT NULL,
346 + source_id BIGINT NOT NULL,
347 + doc_source VARCHAR(255) NULL,
348 + name VARCHAR(255) NOT NULL,
349 + file_path VARCHAR(500) NOT NULL,
350 + provider_file_id VARCHAR(255) NULL,
351 + meta_data LONGTEXT NULL,
352 + post_updated_on DATETIME NOT NULL,
353 + date_updated DATETIME NOT NULL,
354 + date_created DATETIME NOT NULL,
355 + PRIMARY KEY (id),
356 + INDEX idx_status (status),
357 + INDEX idx_source (source),
358 + INDEX idx_status_source (status, source)
359 + ) $collate;
360 + CREATE TABLE {$wpdb->prefix}psmsc_acb_sessions (
361 + id BIGINT NOT NULL AUTO_INCREMENT,
362 + session_id CHAR(36) NOT NULL,
363 + visitor_id CHAR(36) NOT NULL,
364 + subject LONGTEXT NULL,
365 + provider VARCHAR(20) NOT NULL DEFAULT '',
366 + reaction tinyint(1) NULL,
367 + ticket_id BIGINT NULL,
368 + status tinyint(1) NOT NULL,
369 + token_count INT UNSIGNED NOT NULL,
370 + last_activity DATETIME NOT NULL,
371 + date_created DATETIME NOT NULL,
372 + PRIMARY KEY (id),
373 + UNIQUE KEY session_id (session_id),
374 + KEY visitor_id (visitor_id),
375 + KEY status (status),
376 + KEY date_created (date_created)
377 + ) $collate;
378 + CREATE TABLE {$wpdb->prefix}psmsc_acb_messages (
379 + id BIGINT NOT NULL AUTO_INCREMENT,
380 + session_id CHAR(36) NOT NULL,
381 + sender VARCHAR(10) NOT NULL,
382 + message LONGTEXT NULL,
383 + token_count INT UNSIGNED NOT NULL,
384 + date_created DATETIME NOT NULL,
385 + PRIMARY KEY (id),
386 + KEY session_id (session_id),
387 + KEY session_messages (session_id, date_created)
388 + ) $collate;
326 389 ";
327 390
328 391 dbDelta( $tables );
329 392
@@ -335,8 +398,24 @@
335 398 // Copy structure of psmsc_thread table to psmsc_archived_threads table.
336 399 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}psmsc_archived_threads'" ) !== "{$wpdb->prefix}psmsc_archived_threads" ) {
337 400 $wpdb->query( "CREATE TABLE {$wpdb->prefix}psmsc_archived_threads LIKE {$wpdb->prefix}psmsc_threads" );
338 401 }
402 +
403 + // AI assistant feature: add 'ticket_summary' column to tickets and archived tickets table.
404 + $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}psmsc_tickets LIKE 'ticket_summary'" );
405 + if ( empty( $column_exists ) ) {
406 + $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_tickets ADD ticket_summary TEXT NULL" );
407 + }
408 + $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}psmsc_archived_tickets LIKE 'ticket_summary'" );
409 + if ( empty( $column_exists ) ) {
410 + $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_archived_tickets ADD ticket_summary TEXT NULL" );
411 + }
412 +
413 + // Attachment ownership: add 'uploaded_by' column to attachments table.
414 + $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}psmsc_attachments LIKE 'uploaded_by'" );
415 + if ( empty( $column_exists ) ) {
416 + $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_attachments ADD uploaded_by VARCHAR(64) NOT NULL DEFAULT ''" );
417 + }
339 418 }
340 419
341 420 /**
342 421 * First time installation
@@ -745,104 +824,110 @@
745 824 )
746 825 );
747 826 $string_translations[ 'wpsc-cf-name-' . $wpdb->insert_id ] = $name;
748 827
749 - // Insert default category.
750 - $name = __( 'General', 'supportcandy' );
751 - $wpdb->insert(
752 - $wpdb->prefix . 'psmsc_categories',
753 - array(
754 - 'name' => $name,
755 - 'load_order' => 1,
756 - )
757 - );
758 - $string_translations[ 'wpsc-category-' . $wpdb->insert_id ] = $name;
828 + // Insert default category, guarded against re-running initial_setup().
829 + if ( ! $wpdb->get_var( "SELECT id FROM {$wpdb->prefix}psmsc_categories LIMIT 1" ) ) {
830 + $name = __( 'General', 'supportcandy' );
831 + $wpdb->insert(
832 + $wpdb->prefix . 'psmsc_categories',
833 + array(
834 + 'name' => $name,
835 + 'load_order' => 1,
836 + )
837 + );
838 + $string_translations[ 'wpsc-category-' . $wpdb->insert_id ] = $name;
839 + }
759 840
760 - // Insert default statuses.
761 - $name = __( 'Open', 'supportcandy' );
762 - $wpdb->insert(
763 - $wpdb->prefix . 'psmsc_statuses',
764 - array(
765 - 'name' => $name,
766 - 'color' => '#ec1010',
767 - 'bg_color' => '#ffe7e1',
768 - 'load_order' => 1,
769 - )
770 - );
771 - $string_translations[ 'wpsc-status-' . $wpdb->insert_id ] = $name;
841 + // Insert default statuses, guarded against re-running initial_setup().
842 + if ( ! $wpdb->get_var( "SELECT id FROM {$wpdb->prefix}psmsc_statuses LIMIT 1" ) ) {
843 + $name = __( 'Open', 'supportcandy' );
844 + $wpdb->insert(
845 + $wpdb->prefix . 'psmsc_statuses',
846 + array(
847 + 'name' => $name,
848 + 'color' => '#ec1010',
849 + 'bg_color' => '#ffe7e1',
850 + 'load_order' => 1,
851 + )
852 + );
853 + $string_translations[ 'wpsc-status-' . $wpdb->insert_id ] = $name;
772 854
773 - $name = __( 'Awaiting customer reply', 'supportcandy' );
774 - $wpdb->insert(
775 - $wpdb->prefix . 'psmsc_statuses',
776 - array(
777 - 'name' => $name,
778 - 'color' => '#8222E6',
779 - 'bg_color' => '#E9D3FF',
780 - 'load_order' => 2,
781 - )
782 - );
783 - $string_translations[ 'wpsc-status-' . $wpdb->insert_id ] = $name;
855 + $name = __( 'Awaiting customer reply', 'supportcandy' );
856 + $wpdb->insert(
857 + $wpdb->prefix . 'psmsc_statuses',
858 + array(
859 + 'name' => $name,
860 + 'color' => '#8222E6',
861 + 'bg_color' => '#E9D3FF',
862 + 'load_order' => 2,
863 + )
864 + );
865 + $string_translations[ 'wpsc-status-' . $wpdb->insert_id ] = $name;
784 866
785 - $name = __( 'Awaiting agent reply', 'supportcandy' );
786 - $wpdb->insert(
787 - $wpdb->prefix . 'psmsc_statuses',
788 - array(
789 - 'name' => $name,
790 - 'color' => '#EB961C',
791 - 'bg_color' => '#FFEBCE',
792 - 'load_order' => 3,
793 - )
794 - );
795 - $string_translations[ 'wpsc-status-' . $wpdb->insert_id ] = $name;
867 + $name = __( 'Awaiting agent reply', 'supportcandy' );
868 + $wpdb->insert(
869 + $wpdb->prefix . 'psmsc_statuses',
870 + array(
871 + 'name' => $name,
872 + 'color' => '#EB961C',
873 + 'bg_color' => '#FFEBCE',
874 + 'load_order' => 3,
875 + )
876 + );
877 + $string_translations[ 'wpsc-status-' . $wpdb->insert_id ] = $name;
796 878
797 - $name = __( 'Closed', 'supportcandy' );
798 - $wpdb->insert(
799 - $wpdb->prefix . 'psmsc_statuses',
800 - array(
801 - 'name' => $name,
802 - 'color' => '#22940d',
803 - 'bg_color' => '#c1ffcf',
804 - 'load_order' => 4,
805 - )
806 - );
807 - $string_translations[ 'wpsc-status-' . $wpdb->insert_id ] = $name;
879 + $name = __( 'Closed', 'supportcandy' );
880 + $wpdb->insert(
881 + $wpdb->prefix . 'psmsc_statuses',
882 + array(
883 + 'name' => $name,
884 + 'color' => '#22940d',
885 + 'bg_color' => '#c1ffcf',
886 + 'load_order' => 4,
887 + )
888 + );
889 + $string_translations[ 'wpsc-status-' . $wpdb->insert_id ] = $name;
890 + }
808 891
809 - // Insert default priorities.
810 - $name = __( 'Low', 'supportcandy' );
811 - $wpdb->insert(
812 - $wpdb->prefix . 'psmsc_priorities',
813 - array(
814 - 'name' => $name,
815 - 'color' => '#22940d',
816 - 'bg_color' => '#c1ffcf',
817 - 'load_order' => 1,
818 - )
819 - );
820 - $string_translations[ 'wpsc-priority-' . $wpdb->insert_id ] = $name;
892 + // Insert default priorities, guarded against re-running initial_setup().
893 + if ( ! $wpdb->get_var( "SELECT id FROM {$wpdb->prefix}psmsc_priorities LIMIT 1" ) ) {
894 + $name = __( 'Low', 'supportcandy' );
895 + $wpdb->insert(
896 + $wpdb->prefix . 'psmsc_priorities',
897 + array(
898 + 'name' => $name,
899 + 'color' => '#22940d',
900 + 'bg_color' => '#c1ffcf',
901 + 'load_order' => 1,
902 + )
903 + );
904 + $string_translations[ 'wpsc-priority-' . $wpdb->insert_id ] = $name;
821 905
822 - $name = __( 'Medium', 'supportcandy' );
823 - $wpdb->insert(
824 - $wpdb->prefix . 'psmsc_priorities',
825 - array(
826 - 'name' => $name,
827 - 'color' => '#EB961C',
828 - 'bg_color' => '#FFEBCE',
829 - 'load_order' => 2,
830 - )
831 - );
832 - $string_translations[ 'wpsc-priority-' . $wpdb->insert_id ] = $name;
906 + $name = __( 'Medium', 'supportcandy' );
907 + $wpdb->insert(
908 + $wpdb->prefix . 'psmsc_priorities',
909 + array(
910 + 'name' => $name,
911 + 'color' => '#EB961C',
912 + 'bg_color' => '#FFEBCE',
913 + 'load_order' => 2,
914 + )
915 + );
916 + $string_translations[ 'wpsc-priority-' . $wpdb->insert_id ] = $name;
833 917
834 - $name = __( 'High', 'supportcandy' );
835 - $wpdb->insert(
836 - $wpdb->prefix . 'psmsc_priorities',
837 - array(
838 - 'name' => $name,
839 - 'color' => '#ec1010',
840 - 'bg_color' => '#ffe7e1',
841 - 'load_order' => 3,
842 - )
843 - );
844 - $string_translations[ 'wpsc-priority-' . $wpdb->insert_id ] = $name;
918 + $name = __( 'High', 'supportcandy' );
919 + $wpdb->insert(
920 + $wpdb->prefix . 'psmsc_priorities',
921 + array(
922 + 'name' => $name,
923 + 'color' => '#ec1010',
924 + 'bg_color' => '#ffe7e1',
925 + 'load_order' => 3,
926 + )
927 + );
928 + $string_translations[ 'wpsc-priority-' . $wpdb->insert_id ] = $name;
929 + }
845 930
846 931 // company working hrs.
847 932 for ( $i = 1; $i <= 7; $i++ ) {
848 933 $wpdb->insert(
@@ -921,8 +1006,9 @@
921 1006 'ar-assigned-others' => true,
922 1007
923 1008 'backend-access' => true, // Dashboard support menu access.
924 1009 'create-as' => true, // Create ticket on others behalf.
1010 + 'dtt-access' => true, // Deleted ticket access.
925 1011 'eci-access' => true, // Edit customer info.
926 1012 'dash-access' => true, // Dashboard access.
927 1013 'at-access' => true, // Archive tickets menu access.
928 1014 'at-delete-access' => true, // Archive tickets delete access.
@@ -997,8 +1083,9 @@
997 1083 'ar-assigned-others' => false,
998 1084
999 1085 'backend-access' => true,
1000 1086 'create-as' => true,
1087 + 'dtt-access' => false,
1001 1088 'eci-access' => false,
1002 1089 'dash-access' => true, // Dashboard access.
1003 1090 'at-access' => false, // Archive tickets menu access.
1004 1091 'at-delete-access' => false, // Archive tickets delete access.
@@ -1133,8 +1220,12 @@
1133 1220 'closed' => array(
1134 1221 'label' => $labels['closed'],
1135 1222 'is_enable' => 1,
1136 1223 ),
1224 + 'deleted' => array(
1225 + 'label' => $labels['deleted'],
1226 + 'is_enable' => 1,
1227 + ),
1137 1228 )
1138 1229 );
1139 1230
1140 1231 // customer ticket list.
@@ -1473,12 +1564,14 @@
1473 1564
1474 1565 update_option(
1475 1566 'wpsc-recaptcha-settings',
1476 1567 array(
1477 - 'allow-recaptcha' => 0,
1478 - 'recaptcha-version' => 3,
1479 - 'recaptcha-site-key' => '',
1480 - 'recaptcha-secret-key' => '',
1568 + 'captcha-provider' => 0,
1569 + 'recaptcha-version' => 3,
1570 + 'recaptcha-site-key' => '',
1571 + 'recaptcha-secret-key' => '',
1572 + 'cloudflare-site-key' => '',
1573 + 'cloudflare-secret-key' => '',
1481 1574 )
1482 1575 );
1483 1576
1484 1577 update_option(
@@ -1500,8 +1593,10 @@
1500 1593 'auto-archive-tickets-time' => 0,
1501 1594 'auto-archive-tickets-unit' => 'days',
1502 1595 'permanent-archive-tickets-time' => 0,
1503 1596 'permanent-archive-tickets-unit' => 'days',
1597 + 'permanent-delete-tickets-time' => 0,
1598 + 'permanent-delete-tickets-unit' => 'days',
1504 1599 'allow-bcc' => 0,
1505 1600 'allow-cc' => 0,
1506 1601 'view-more' => 1,
1507 1602 'allow-reply-to-close-ticket' => array( 'customer', 'agent' ),
@@ -1532,8 +1627,9 @@
1532 1627 );
1533 1628
1534 1629 // ticket widgets.
1535 1630 $labels = array(
1631 + 'agent-collision' => __( 'Currently viewing', 'supportcandy' ),
1536 1632 'change-status' => __( 'Ticket status', 'supportcandy' ),
1537 1633 'raised-by' => __( 'Customer', 'supportcandy' ),
1538 1634 'ticket-info' => __( 'Ticket info', 'supportcandy' ),
1539 1635 'assignee' => __( 'Assignee', 'supportcandy' ),
@@ -1548,17 +1644,25 @@
1548 1644 }
1549 1645 update_option(
1550 1646 'wpsc-ticket-widget',
1551 1647 array(
1552 - 'change-status' => array(
1553 - 'title' => $labels['change-status'],
1648 + 'agent-collision' => array(
1649 + 'title' => $labels['agent-collision'],
1554 1650 'is_enable' => 1,
1555 - 'allow-customer' => 1,
1651 + 'allow-customer' => 0,
1556 1652 'allowed-agent-roles' => array( 1, 2 ),
1557 1653 'show-priority-to-customer' => 0,
1558 - 'callback' => 'wpsc_get_tw_ticket_status()',
1559 - 'class' => 'WPSC_ITW_Change_Status',
1654 + 'callback' => 'wpsc_get_tw_agent_collision()',
1655 + 'class' => 'WPSC_ITW_Agent_Collision',
1560 1656 ),
1657 + 'change-status' => array(
1658 + 'title' => $labels['change-status'],
1659 + 'is_enable' => 1,
1660 + 'allow-customer' => 1,
1661 + 'allowed-agent-roles' => array( 1, 2 ),
1662 + 'callback' => 'wpsc_get_tw_ticket_status()',
1663 + 'class' => 'WPSC_ITW_Change_Status',
1664 + ),
1561 1665 'raised-by' => array(
1562 1666 'title' => $labels['raised-by'],
1563 1667 'is_enable' => 1,
1564 1668 'allow-customer' => 0,
@@ -1715,15 +1819,8 @@
1715 1819 'z-index' => 900000000,
1716 1820 )
1717 1821 );
1718 1822 update_option(
1719 - 'wpsc-ap-agent-collision',
1720 - array(
1721 - 'header-bg-color' => '#e6e6e6',
1722 - 'header-text-color' => '#2c3e50',
1723 - )
1724 - );
1725 - update_option(
1726 1823 'wpsc-ap-ticket-list',
1727 1824 array(
1728 1825 'list-header-background-color' => '#2c3e50',
1729 1826 'list-header-text-color' => '#fff',
@@ -1748,12 +1845,12 @@
1748 1845 // dashboard general setting.
1749 1846 update_option(
1750 1847 'wpsc-ap-dashboard',
1751 1848 array(
1752 - 'card-body-bg-color' => '#f9f9f9',
1849 + 'card-body-bg-color' => '#ffffff',
1753 1850 'card-body-svg-color' => '#777',
1754 1851 'card-body-text-color' => '#2c3e50',
1755 - 'widget-body-bg-color' => '#f9f9f9',
1852 + 'widget-body-bg-color' => '#ffffff',
1756 1853 'widget-body-svg-color' => '#777',
1757 1854 'widget-body-text-color' => '#2c3e50',
1758 1855 )
1759 1856 );
@@ -1940,8 +2037,83 @@
1940 2037 self::create_indexing();
1941 2038
1942 2039 // update string translations.
1943 2040 update_option( 'wpsc-string-translation', $string_translations );
2041 +
2042 + // Add AI assistant option.
2043 + update_option(
2044 + 'wpsc-ps-ai-assistant-settings',
2045 + array(
2046 + 'status' => '0',
2047 + 'provider' => 'openai',
2048 + 'api_key' => '',
2049 + 'model' => 'gpt-4o-mini',
2050 + 'max-tokens' => 500,
2051 + 'auto-delete-ai-logs-time' => 1,
2052 + 'auto-delete-ai-logs-unit' => 'year',
2053 + 'custom-prompt' => '',
2054 + 'summary-custom-prompt' => '',
2055 + 'auto-draft-custom-prompt' => '',
2056 + 'is-active' => 0,
2057 + 'last-error' => '',
2058 + 'ai-max-upload-file-size' => 10,
2059 + )
2060 + );
2061 +
2062 + // update AI training options.
2063 + update_option(
2064 + 'wpsc-ps-ai-training-sources',
2065 + array(
2066 + array(
2067 + 'slug' => 'local',
2068 + 'type' => 'localhost',
2069 + 'name' => 'Local',
2070 + 'api-url' => rest_url(),
2071 + 'post-types' => array(),
2072 + ),
2073 + ),
2074 + );
2075 +
2076 + // default AI chatbot settings.
2077 + update_option(
2078 + 'wpsc-ps-acb-chatbot-settings',
2079 + array(
2080 + 'status' => '0',
2081 + 'delete-acb-session-time' => 1,
2082 + 'delete-acb-session-unit' => 'year',
2083 + 'show-footer-branding' => '1',
2084 + 'sessions-per-page' => 30,
2085 + 'popup-delay-status' => '0',
2086 + 'popup-delay' => 10,
2087 + 'popup-display-limit' => 3,
2088 + 'custom-prompt' => '',
2089 + )
2090 + );
2091 +
2092 + // default chatbot appearance settings.
2093 + update_option(
2094 + 'wpsc-acb-appearance-general',
2095 + array(
2096 + 'background-color' => '#2271b1',
2097 + 'icon-color' => '#ffffff',
2098 + 'header-text' => __( 'AI Chatbot', 'supportcandy' ),
2099 + 'greeting-text' => __( 'Hey, I\'m your assistant. How can I help you today?', 'supportcandy' ),
2100 + 'warning-text' => __( 'AI can make mistakes. Check important info.', 'supportcandy' ),
2101 + )
2102 + );
2103 +
2104 + // Onboarding wizard: seed pending state for this fresh install and flag a
2105 + // one-time redirect so WPSC_Onboarding shows the wizard on the next admin
2106 + // page load. Existing sites being upgraded never reach this method, so the
2107 + // wizard never auto-appears for them.
2108 + update_option(
2109 + 'wpsc-onboarding-wizard',
2110 + array(
2111 + 'status' => 'pending',
2112 + 'steps' => array(),
2113 + )
2114 + );
2115 + set_transient( 'wpsc_onboarding_redirect', 1, 5 * MINUTE_IN_SECONDS );
1944 2116 }
1945 2117
1946 2118 /**
1947 2119 * Upgrade the version
@@ -2245,9 +2417,9 @@
2245 2417
2246 2418 if ( version_compare( self::$current_version, '3.2.4', '<' ) ) {
2247 2419
2248 2420 // add default true to admin and agent dashboard access.
2249 - $roles = get_option( 'wpsc-agent-roles' );
2421 + $roles = get_option( 'wpsc-agent-roles', array() );
2250 2422 $role_keys = array();
2251 2423 foreach ( $roles as $key => $role ) {
2252 2424 $role['caps']['dash-access'] = true;
2253 2425 $roles[ $key ] = $role;
@@ -2406,12 +2578,12 @@
2406 2578 // dashboard appreance general setting.
2407 2579 update_option(
2408 2580 'wpsc-ap-dashboard',
2409 2581 array(
2410 - 'card-body-bg-color' => '#f9f9f9',
2582 + 'card-body-bg-color' => '#ffffff',
2411 2583 'card-body-svg-color' => '#777',
2412 2584 'card-body-text-color' => '#2c3e50',
2413 - 'widget-body-bg-color' => '#f9f9f9',
2585 + 'widget-body-bg-color' => '#ffffff',
2414 2586 'widget-body-svg-color' => '#777',
2415 2587 'widget-body-text-color' => '#2c3e50',
2416 2588 )
2417 2589 );
@@ -2583,15 +2755,8 @@
2583 2755 }
2584 2756
2585 2757 if ( version_compare( self::$current_version, '3.4.2', '<' ) ) {
2586 2758
2587 - // Remove "delete" filter.
2588 - $atl_filters = get_option( 'wpsc-atl-default-filters', array() );
2589 - if ( is_array( $atl_filters ) && isset( $atl_filters['deleted'] ) ) {
2590 - unset( $atl_filters['deleted'] );
2591 - update_option( 'wpsc-atl-default-filters', $atl_filters );
2592 - }
2593 -
2594 2759 // add advanced settings for archive tickets.
2595 2760 $advanced = get_option( 'wpsc-ms-advanced-settings' );
2596 2761 $advanced['auto-archive-tickets-time'] = $advanced['auto-delete-tickets-time'] ?? 0;
2597 2762 $advanced['auto-archive-tickets-unit'] = $advanced['auto-delete-tickets-unit'] ?? 'days';
@@ -2599,10 +2764,8 @@
2599 2764 $advanced['permanent-archive-tickets-unit'] = $advanced['permanent-delete-tickets-unit'] ?? 'days';
2600 2765 unset(
2601 2766 $advanced['auto-delete-tickets-time'],
2602 2767 $advanced['auto-delete-tickets-unit'],
2603 - $advanced['permanent-delete-tickets-time'],
2604 - $advanced['permanent-delete-tickets-unit']
2605 2768 );
2606 2769 update_option( 'wpsc-ms-advanced-settings', $advanced );
2607 2770
2608 2771 // Remove 'dtt-access' and set new capabilities while preserving role keys.
@@ -2608,22 +2771,14 @@
2608 2771 // Remove 'dtt-access' and set new capabilities while preserving role keys.
2609 2772 $roles = get_option( 'wpsc-agent-roles', array() );
2610 2773 if ( ! empty( $roles ) && is_array( $roles ) ) {
2611 2774 foreach ( $roles as $key => &$role ) {
2612 -
2613 2775 // Ensure caps array exists.
2614 2776 if ( empty( $role['caps'] ) || ! is_array( $role['caps'] ) ) {
2615 2777 $role['caps'] = array();
2616 2778 }
2617 -
2618 2779 // Bydefault only Admin (1) has access to archive tickets.
2619 2780 $is_enabled = ( $key == 1 );
2620 -
2621 - // Set delete ticket capability true for admin and for rest 0.
2622 - $role['caps']['dtt-unassigned'] = $is_enabled;
2623 - $role['caps']['dtt-assigned-me'] = $is_enabled;
2624 - $role['caps']['dtt-assigned-others'] = $is_enabled;
2625 -
2626 2781 // Set new capabilities.
2627 2782 $role['caps']['at-unassigned'] = $is_enabled;
2628 2783 $role['caps']['at-assigned-me'] = $is_enabled;
2629 2784 $role['caps']['at-assigned-others'] = $is_enabled;
@@ -2628,31 +2783,285 @@
2628 2783 $role['caps']['at-assigned-me'] = $is_enabled;
2629 2784 $role['caps']['at-assigned-others'] = $is_enabled;
2630 2785 $role['caps']['at-access'] = $is_enabled;
2631 2786 $role['caps']['at-delete-access'] = $is_enabled;
2787 + }
2788 + update_option( 'wpsc-agent-roles', $roles );
2789 + }
2790 + }
2632 2791
2633 - // Remove deprecated capability.
2634 - unset( $role['caps']['dtt-access'] );
2792 + if ( version_compare( self::$current_version, '3.4.4', '<' ) ) {
2793 +
2794 + $atl_filters = get_option( 'wpsc-atl-default-filters', array() );
2795 + $atl_filters['deleted'] = array(
2796 + 'label' => __( 'Deleted', 'supportcandy' ),
2797 + 'is_enable' => 1,
2798 + );
2799 + update_option( 'wpsc-atl-default-filters', $atl_filters );
2800 +
2801 + // Add capability for delete ticket filter access.
2802 + $roles = get_option( 'wpsc-agent-roles', array() );
2803 + foreach ( $roles as $key => &$role ) {
2804 + $is_enabled = ( $key == 1 );
2805 + $roles[ $key ]['caps']['dtt-access'] = $is_enabled;
2806 + }
2807 + update_option( 'wpsc-agent-roles', $roles );
2808 +
2809 + // permanently delete tickets.
2810 + $advanced = get_option( 'wpsc-ms-advanced-settings' );
2811 + $advanced['permanent-delete-tickets-time'] = $advanced['permanent-archive-tickets-time'] ?? 0;
2812 + $advanced['permanent-delete-tickets-unit'] = $advanced['permanent-archive-tickets-unit'] ?? 'days';
2813 + update_option( 'wpsc-ms-advanced-settings', $advanced );
2814 +
2815 + // clear old scheduled task.
2816 + wp_clear_scheduled_hook( 'wpsc_permanently_delete_archive_tickets' );
2817 + wp_clear_scheduled_hook( 'wpsc_permenently_delete_tickets' );
2818 + wp_clear_scheduled_hook( 'wpsc_auto_archive_closed_tickets' );
2819 + }
2820 +
2821 + if ( version_compare( self::$current_version, '3.4.7', '<' ) ) {
2822 +
2823 + $widgets = get_option( 'wpsc-ticket-widget', array() );
2824 + if ( ! isset( $widgets['agent-collision'] ) ) {
2825 +
2826 + $roles = get_option( 'wpsc-agent-roles', array() );
2827 + $role_keys = array();
2828 + foreach ( $roles as $key => $role ) {
2829 + $role_keys[] = $key;
2635 2830 }
2636 - update_option( 'wpsc-agent-roles', $roles );
2831 +
2832 + $label = esc_attr__( 'Currently viewing', 'supportcandy' );
2833 + $agent_collision = array(
2834 + 'agent-collision' => array(
2835 + 'title' => $label,
2836 + 'is_enable' => 1,
2837 + 'allow-customer' => 0,
2838 + 'allowed-agent-roles' => $role_keys,
2839 + 'show-priority-to-customer' => 0,
2840 + 'callback' => 'wpsc_get_tw_agent_collision()',
2841 + 'class' => 'WPSC_ITW_Agent_Collision',
2842 + ),
2843 + );
2844 +
2845 + // append the widget on the top.
2846 + $widgets = array_merge( $agent_collision, $widgets );
2847 + update_option( 'wpsc-ticket-widget', $widgets );
2848 +
2849 + // string translations.
2850 + $string_translations = get_option( 'wpsc-string-translation' );
2851 + $string_translations['wpsc-twt-agent-collision'] = $label;
2852 + update_option( 'wpsc-string-translation', $string_translations );
2637 2853 }
2854 + }
2638 2855
2639 - // scheduled task for moving deleted tickets to archived tickets.
2640 - if ( ! $wpdb->get_var( "SELECT id FROM {$wpdb->prefix}psmsc_scheduled_tasks WHERE class='WPSC_SC_Upgrade' AND method='archive_deleted_tickets'" ) ) {
2641 - $wpdb->insert(
2642 - $wpdb->prefix . 'psmsc_scheduled_tasks',
2856 + if ( version_compare( self::$current_version, '3.4.8', '<' ) ) {
2857 +
2858 + $recaptcha = get_option( 'wpsc-recaptcha-settings' );
2859 + $recaptcha['captcha-provider'] = $recaptcha['allow-recaptcha'] ? 'google-recaptcha' : '0';
2860 + $recaptcha['cloudflare-site-key'] = '';
2861 + $recaptcha['cloudflare-secret-key'] = '';
2862 + unset( $recaptcha['allow-recaptcha'] );
2863 + update_option( 'wpsc-recaptcha-settings', $recaptcha );
2864 + }
2865 +
2866 + if ( version_compare( self::$current_version, '3.5.1', '<' ) ) {
2867 +
2868 + // AI assistant feature moved into core - seed defaults for sites upgrading from an older core version.
2869 + if ( false === get_option( 'wpsc-ps-ai-assistant-settings', false ) ) {
2870 + update_option(
2871 + 'wpsc-ps-ai-assistant-settings',
2643 2872 array(
2644 - 'class' => 'WPSC_SC_Upgrade',
2645 - 'method' => 'archive_deleted_tickets',
2646 - 'is_manual' => 1,
2647 - 'warning_text' => 'SupportCandy - Database upgrade needed to move deleted tickets to archive.',
2648 - 'warning_link_text' => 'Upgrade Now',
2649 - 'progressbar_text' => 'Moving tickets is in progress...',
2873 + 'status' => '0',
2874 + 'provider' => 'openai',
2875 + 'api_key' => '',
2876 + 'model' => 'gpt-4o-mini',
2877 + 'max-tokens' => 500,
2878 + 'auto-delete-ai-logs-time' => 1,
2879 + 'auto-delete-ai-logs-unit' => 'year',
2880 + 'custom-prompt' => '',
2881 + 'summary-custom-prompt' => '',
2882 + 'auto-draft-custom-prompt' => '',
2883 + 'is-active' => 0,
2884 + 'last-error' => '',
2885 + 'ai-max-upload-file-size' => 10,
2650 2886 )
2651 2887 );
2652 2888 }
2889 +
2890 + if ( false === get_option( 'wpsc-ps-ai-training-sources', false ) ) {
2891 + update_option(
2892 + 'wpsc-ps-ai-training-sources',
2893 + array(
2894 + array(
2895 + 'slug' => 'local',
2896 + 'type' => 'localhost',
2897 + 'name' => 'Local',
2898 + 'api-url' => rest_url(),
2899 + 'post-types' => array(),
2900 + ),
2901 + ),
2902 + );
2903 + }
2904 +
2905 + // AI chatbot feature moved into core - seed defaults for sites upgrading from an older core version.
2906 + if ( false === get_option( 'wpsc-ps-acb-chatbot-settings', false ) ) {
2907 + update_option(
2908 + 'wpsc-ps-acb-chatbot-settings',
2909 + array(
2910 + 'status' => '0',
2911 + 'delete-acb-session-time' => 1,
2912 + 'delete-acb-session-unit' => 'year',
2913 + 'show-footer-branding' => '1',
2914 + 'sessions-per-page' => 30,
2915 + 'popup-delay-status' => '0',
2916 + 'popup-delay' => 10,
2917 + 'popup-display-limit' => 3,
2918 + 'custom-prompt' => '',
2919 + )
2920 + );
2921 + }
2922 +
2923 + if ( false === get_option( 'wpsc-acb-appearance-general', false ) ) {
2924 + update_option(
2925 + 'wpsc-acb-appearance-general',
2926 + array(
2927 + 'background-color' => '#2271b1',
2928 + 'icon-color' => '#ffffff',
2929 + 'header-text' => __( 'AI Chatbot', 'supportcandy' ),
2930 + 'greeting-text' => __( 'Hey, I\'m your assistant. How can I help you today?', 'supportcandy' ),
2931 + 'warning-text' => __( 'AI can make mistakes. Check important info.', 'supportcandy' ),
2932 + )
2933 + );
2934 + }
2935 +
2936 + // Migrate legacy AI training data - only for sites that already used AI assistant / AI chatbot via Productivity Suite.
2937 + $ps_settings = get_option( 'wpsc-ps-settings', array() );
2938 +
2939 + $wpdb->query( "UPDATE {$wpdb->prefix}psmsc_ai_training SET doc_source = 'local' WHERE source NOT IN ( 'file', 'url', 'ticket' )" );
2940 + $wpdb->query( "UPDATE {$wpdb->prefix}psmsc_ai_training SET doc_source = '' WHERE source IN ( 'file', 'url', 'ticket' )" );
2941 +
2942 + // update AI training options.
2943 + $local_post_types = array(
2944 + array(
2945 + 'slug' => 'post',
2946 + 'name' => 'Posts',
2947 + 'status' => 0,
2948 + 'endpoint' => rest_url( 'wp/v2/posts' ),
2949 + ),
2950 + array(
2951 + 'slug' => 'page',
2952 + 'name' => 'Pages',
2953 + 'status' => 0,
2954 + 'endpoint' => rest_url( 'wp/v2/pages' ),
2955 + ),
2956 + );
2957 +
2958 + $bd_settings = get_option( 'wpsc-ps-ai-bd-settings', array() );
2959 + if ( isset( $bd_settings['bd-status'] ) && $bd_settings['bd-status'] == 1 ) {
2960 + $local_post_types[] = array(
2961 + 'slug' => 'docs',
2962 + 'name' => 'Betterdocs',
2963 + 'status' => 1,
2964 + 'endpoint' => rest_url( 'wp/v2/docs' ),
2965 + );
2966 +
2967 + // Update source column for betterdocs source.
2968 + $wpdb->query( "UPDATE {$wpdb->prefix}psmsc_ai_training SET source = 'docs' WHERE source = 'betterdocs'" );
2969 + }
2970 +
2971 + update_option(
2972 + 'wpsc-ps-ai-training-sources',
2973 + array(
2974 + array(
2975 + 'slug' => 'local',
2976 + 'type' => 'localhost',
2977 + 'name' => 'Local',
2978 + 'api-url' => rest_url(),
2979 + 'post-types' => $local_post_types,
2980 + ),
2981 + ),
2982 + );
2983 +
2984 + // wpsc_ps_ai_categories is no longer used - post-type training sources are
2985 + // now routed directly by source type instead of through a categories registry.
2986 + delete_option( 'wpsc_ps_ai_categories' );
2987 +
2988 + // remove 'category', 'is_expire' and 'expiry_date' columns from AI training table.
2989 + foreach ( array( 'category', 'is_expire', 'expiry_date' ) as $column ) {
2990 + $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}psmsc_ai_training LIKE '{$column}'" );
2991 + if ( ! empty( $column_exists ) ) {
2992 + $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_ai_training DROP COLUMN {$column}" );
2993 + }
2994 + }
2995 +
2996 + // unschedule expired training cleanup cron, this feature has been removed.
2997 + wp_clear_scheduled_hook( 'wpsc_auto_delete_ai_training_expired_records' );
2998 +
2999 + // remove 'manage-ai-training' capability, this is no longer used.
3000 + $roles = get_option( 'wpsc-agent-roles', array() );
3001 + foreach ( $roles as $key => $role ) {
3002 + unset( $role['caps']['manage-ai-training'] );
3003 + $roles[ $key ] = $role;
3004 + }
3005 + update_option( 'wpsc-agent-roles', $roles );
3006 +
3007 + if ( isset( $ai_assistant['is-active'] ) ) {
3008 + $ai_assistant = get_option( 'wpsc-ps-ai-assistant-settings' );
3009 + $ai_assistant['status'] = $ai_assistant['is-active'] ? 1 : 0;
3010 + update_option( 'wpsc-ps-ai-assistant-settings', $ai_assistant );
3011 + }
3012 +
3013 + // enable chatbot feature.
3014 + if ( isset( $ps_settings['ai-chatbot'] ) ) {
3015 + $chatbot = get_option( 'wpsc-ps-acb-chatbot-settings' );
3016 + $chatbot['status'] = $ps_settings['ai-chatbot'] ? 1 : 0;
3017 + update_option( 'wpsc-ps-acb-chatbot-settings', $chatbot );
3018 + }
2653 3019 }
2654 3020
3021 + if ( version_compare( self::$current_version, '3.5.2', '<' ) ) {
3022 +
3023 + $chatbot = get_option( 'wpsc-ps-acb-chatbot-settings' );
3024 + $chatbot['popup-delay-status'] = '0';
3025 + $chatbot['popup-delay'] = 10;
3026 + $chatbot['popup-display-limit'] = 3;
3027 + update_option( 'wpsc-ps-acb-chatbot-settings', $chatbot );
3028 + }
3029 +
3030 + if ( version_compare( self::$current_version, '3.5.3', '<' ) ) {
3031 +
3032 + $chatbot = get_option( 'wpsc-ps-acb-chatbot-settings' );
3033 + $chatbot['custom-prompt'] = '';
3034 + update_option( 'wpsc-ps-acb-chatbot-settings', $chatbot );
3035 + }
3036 +
3037 + if ( version_compare( self::$current_version, '3.5.4', '<' ) ) {
3038 +
3039 + $appearance = get_option( 'wpsc-acb-appearance-general' );
3040 + if ( is_array( $appearance ) ) {
3041 + if ( ! isset( $appearance['header-text'] ) ) {
3042 + $appearance['header-text'] = __( 'AI Chatbot', 'supportcandy' );
3043 + }
3044 + if ( ! isset( $appearance['greeting-text'] ) ) {
3045 + $appearance['greeting-text'] = __( 'Hey, I\'m your assistant. How can I help you today?', 'supportcandy' );
3046 + }
3047 + if ( ! isset( $appearance['warning-text'] ) ) {
3048 + $appearance['warning-text'] = __( 'AI can make mistakes. Check important info.', 'supportcandy' );
3049 + }
3050 + update_option( 'wpsc-acb-appearance-general', $appearance );
3051 + }
3052 +
3053 + self::create_indexing();
3054 +
3055 + update_option(
3056 + 'wpsc-onboarding-wizard',
3057 + array(
3058 + 'status' => 'completed',
3059 + 'steps' => array(),
3060 + )
3061 + );
3062 + }
3063 +
2655 3064 update_option( 'wpsc-string-translation', $string_translations );
2656 3065 self::set_upgrade_complete();
2657 3066 }
2658 3067
@@ -2757,8 +3166,20 @@
2757 3166 )
2758 3167 );
2759 3168 if ( ! $index_exists ) {
2760 3169 $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_archived_threads ADD INDEX {$index_name} (ticket)" );
3170 + }
3171 +
3172 + // Create index on AI training table.
3173 + $index_name = 'idx_doc_source_source_provider_source_id';
3174 + $index_exists = $wpdb->get_var(
3175 + $wpdb->prepare(
3176 + "SHOW INDEX FROM {$wpdb->prefix}psmsc_ai_training WHERE Key_name = %s",
3177 + $index_name
3178 + )
3179 + );
3180 + if ( ! $index_exists ) {
3181 + $wpdb->query( "ALTER TABLE {$wpdb->prefix}psmsc_ai_training ADD INDEX {$index_name} (doc_source, source, provider, source_id)" );
2761 3182 }
2762 3183 }
2763 3184 }
2764 3185 endif;