Changeset 3476789
- Timestamp:
- 03/07/2026 12:56:39 AM (3 weeks ago)
- Location:
- visualwebs-ml
- Files:
-
- 16 added
- 28 deleted
- 41 edited
- 2 copied
-
tags/5.5.0 (copied) (copied from visualwebs-ml/trunk)
-
tags/5.5.0/assets/css (deleted)
-
tags/5.5.0/assets/js (deleted)
-
tags/5.5.0/class-visualwebs-ml.php (modified) (5 diffs)
-
tags/5.5.0/composer.json (modified) (1 diff)
-
tags/5.5.0/includes/class-visualwebs-ml-dynamic-pricing-queue-table.php (deleted)
-
tags/5.5.0/includes/class-visualwebs-ml-semantic-search-queue-table.php (deleted)
-
tags/5.5.0/includes/cronjobs.php (modified) (8 diffs)
-
tags/5.5.0/includes/feeds (added)
-
tags/5.5.0/includes/feeds/class-insights-feed-generator.php (added)
-
tags/5.5.0/includes/feeds/class-page-feed-generator.php (added)
-
tags/5.5.0/includes/feeds/class-product-feed-generator.php (added)
-
tags/5.5.0/includes/feeds/class-sales-feed-generator.php (added)
-
tags/5.5.0/includes/hooks/event-dispatcher.php (added)
-
tags/5.5.0/includes/redux-config.php (modified) (9 diffs)
-
tags/5.5.0/readme.txt (copied) (copied from visualwebs-ml/trunk/readme.txt) (2 diffs)
-
tags/5.5.0/templates/dynamic-pricing-predict.php (deleted)
-
tags/5.5.0/templates/dynamic-pricing-queue.php (deleted)
-
tags/5.5.0/templates/dynamic-pricing-train.php (deleted)
-
tags/5.5.0/templates/dynamic-pricing-view.php (deleted)
-
tags/5.5.0/templates/ml-dashboard.php (modified) (3 diffs)
-
tags/5.5.0/templates/ml-widgets.php (deleted)
-
tags/5.5.0/templates/n8n-chatbot-widget.php (added)
-
tags/5.5.0/templates/semantic-search-add.php (deleted)
-
tags/5.5.0/templates/semantic-search-edit.php (deleted)
-
tags/5.5.0/templates/semantic-search-queue.php (deleted)
-
tags/5.5.0/vendor/autoload.php (modified) (1 diff)
-
tags/5.5.0/vendor/composer/autoload_static.php (modified) (3 diffs)
-
tags/5.5.0/vendor/composer/platform_check.php (modified) (1 diff)
-
tags/5.5.0/vendor/visualwebs-ml/Activator.php (modified) (1 diff)
-
tags/5.5.0/vendor/visualwebs-ml/Admin.php (modified) (4 diffs)
-
tags/5.5.0/vendor/visualwebs-ml/Block/Adminhtml/Dashboard.php (modified) (1 diff)
-
tags/5.5.0/vendor/visualwebs-ml/Block/ChatWidget.php (modified) (3 diffs)
-
tags/5.5.0/vendor/visualwebs-ml/Helper/Chatbot.php (modified) (9 diffs)
-
tags/5.5.0/vendor/visualwebs-ml/Helper/ChatbotApiCall.php (modified) (5 diffs)
-
tags/5.5.0/vendor/visualwebs-ml/Helper/Data.php (modified) (6 diffs)
-
tags/5.5.0/vendor/visualwebs-ml/Helper/Enqueue.php (modified) (3 diffs)
-
tags/5.5.0/vendor/visualwebs-ml/Helper/OpenAi.php (deleted)
-
tags/5.5.0/vendor/visualwebs-ml/Helper/Pinecone.php (deleted)
-
tags/5.5.0/vendor/visualwebs-ml/Helper/Visualwebs.php (modified) (1 diff)
-
tags/5.5.0/vendor/visualwebs-ml/Helper/WorkflowSender.php (added)
-
tags/5.5.0/vendor/visualwebs-ml/Model/Api/Visualwebs.php (modified) (1 diff)
-
tags/5.5.0/vendor/visualwebs-ml/Model/MLModel.php (modified) (1 diff)
-
tags/5.5.0/visualwebs-ml.php (modified) (1 diff)
-
trunk/assets/css (deleted)
-
trunk/assets/js (deleted)
-
trunk/class-visualwebs-ml.php (modified) (5 diffs)
-
trunk/composer.json (modified) (1 diff)
-
trunk/includes/class-visualwebs-ml-dynamic-pricing-queue-table.php (deleted)
-
trunk/includes/class-visualwebs-ml-semantic-search-queue-table.php (deleted)
-
trunk/includes/cronjobs.php (modified) (8 diffs)
-
trunk/includes/feeds (added)
-
trunk/includes/feeds/class-insights-feed-generator.php (added)
-
trunk/includes/feeds/class-page-feed-generator.php (added)
-
trunk/includes/feeds/class-product-feed-generator.php (added)
-
trunk/includes/feeds/class-sales-feed-generator.php (added)
-
trunk/includes/hooks/event-dispatcher.php (added)
-
trunk/includes/redux-config.php (modified) (9 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/templates/dynamic-pricing-predict.php (deleted)
-
trunk/templates/dynamic-pricing-queue.php (deleted)
-
trunk/templates/dynamic-pricing-train.php (deleted)
-
trunk/templates/dynamic-pricing-view.php (deleted)
-
trunk/templates/ml-dashboard.php (modified) (3 diffs)
-
trunk/templates/ml-widgets.php (deleted)
-
trunk/templates/n8n-chatbot-widget.php (added)
-
trunk/templates/semantic-search-add.php (deleted)
-
trunk/templates/semantic-search-edit.php (deleted)
-
trunk/templates/semantic-search-queue.php (deleted)
-
trunk/vendor/autoload.php (modified) (1 diff)
-
trunk/vendor/composer/autoload_static.php (modified) (3 diffs)
-
trunk/vendor/composer/platform_check.php (modified) (1 diff)
-
trunk/vendor/visualwebs-ml/Activator.php (modified) (1 diff)
-
trunk/vendor/visualwebs-ml/Admin.php (modified) (4 diffs)
-
trunk/vendor/visualwebs-ml/Block/Adminhtml/Dashboard.php (modified) (1 diff)
-
trunk/vendor/visualwebs-ml/Block/ChatWidget.php (modified) (3 diffs)
-
trunk/vendor/visualwebs-ml/Helper/Chatbot.php (modified) (9 diffs)
-
trunk/vendor/visualwebs-ml/Helper/ChatbotApiCall.php (modified) (5 diffs)
-
trunk/vendor/visualwebs-ml/Helper/Data.php (modified) (6 diffs)
-
trunk/vendor/visualwebs-ml/Helper/Enqueue.php (modified) (3 diffs)
-
trunk/vendor/visualwebs-ml/Helper/OpenAi.php (deleted)
-
trunk/vendor/visualwebs-ml/Helper/Pinecone.php (deleted)
-
trunk/vendor/visualwebs-ml/Helper/Visualwebs.php (modified) (1 diff)
-
trunk/vendor/visualwebs-ml/Helper/WorkflowSender.php (added)
-
trunk/vendor/visualwebs-ml/Model/Api/Visualwebs.php (modified) (1 diff)
-
trunk/vendor/visualwebs-ml/Model/MLModel.php (modified) (1 diff)
-
trunk/visualwebs-ml.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
visualwebs-ml/tags/5.5.0/class-visualwebs-ml.php
r3322994 r3476789 94 94 95 95 add_action( 'plugins_loaded', array( 'Visualwebs\ML\Admin', 'init' ) ); 96 add_action( 'wp_footer', array( $this, 'render_frontend_chatbot_widget' ), 999 ); 96 97 add_action( 97 98 'redux/options/visualwebs_ml_options/saved', … … 135 136 register_rest_route( 136 137 'visualwebs-ml/v1', 138 'chat-api/admin-call', 139 array( 140 'methods' => 'POST', 141 'callback' => array( $this, 'handle_admin_chat_api_call' ), 142 'permission_callback' => array( $this, 'check_admin_chat_permissions' ), 143 ) 144 ); 145 146 register_rest_route( 147 'visualwebs-ml/v1', 137 148 'ml-api/call', 138 149 array( … … 156 167 require_once __DIR__ . '/includes/cronjobs.php'; 157 168 require_once __DIR__ . '/includes/hooks/entity-save.php'; 169 require_once __DIR__ . '/includes/hooks/event-dispatcher.php'; 158 170 require_once __DIR__ . '/includes/helpers/content-generator.php'; 171 require_once __DIR__ . '/includes/feeds/class-insights-feed-generator.php'; 159 172 } 160 173 … … 174 187 175 188 /** 189 * Render chatbot widget in frontend footer. 190 * 191 * @since 5.5.0 192 */ 193 public function render_frontend_chatbot_widget() { 194 require plugin_dir_path( __FILE__ ) . 'templates/n8n-chatbot-widget.php'; 195 } 196 197 /** 176 198 * Handle chat API callback 177 199 * … … 189 211 */ 190 212 public function handle_chat_api_call( WP_REST_Request $request ) { 191 $response = $this->chatbot_api_helper->execute( $request->get_query_params() ); 213 $response = $this->chatbot_api_helper->execute( $request->get_json_params() ); 214 return rest_ensure_response( $response ); 215 } 216 217 /** 218 * Validate permissions for admin chatbot endpoint. 219 * 220 * @param WP_REST_Request $request Request object. 221 * @return bool|WP_Error 222 */ 223 public function check_admin_chat_permissions( WP_REST_Request $request ) { 224 if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) { 225 return new WP_Error( 'vwml_forbidden', __( 'You are not allowed to use admin chatbot.', 'visualwebs-ml' ), array( 'status' => 403 ) ); 226 } 227 228 $nonce = sanitize_text_field( (string) $request->get_param( '_vwml_nonce' ) ); 229 if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'visualwebs_ml_admin_chat' ) ) { 230 return new WP_Error( 'vwml_invalid_nonce', __( 'Invalid admin chatbot nonce.', 'visualwebs-ml' ), array( 'status' => 403 ) ); 231 } 232 233 return true; 234 } 235 236 /** 237 * Handle admin chatbot API call routed through secure local endpoint. 238 * 239 * @param WP_REST_Request $request The REST API request object. 240 * @return WP_REST_Response 241 */ 242 public function handle_admin_chat_api_call( WP_REST_Request $request ) { 243 $response = $this->chatbot_api_helper->execute_admin( $request->get_json_params() ); 192 244 return rest_ensure_response( $response ); 193 245 } -
visualwebs-ml/tags/5.5.0/composer.json
r3319473 r3476789 9 9 } 10 10 }, 11 "require": {12 "smalot/pdfparser": "^0.18",13 "phpoffice/phpword": "^1.1"14 },15 11 "scripts": { 16 12 "post-install-cmd": [ -
visualwebs-ml/tags/5.5.0/includes/cronjobs.php
r3330758 r3476789 23 23 use Visualwebs\ML\Helper\Visualwebs as ApiHelper; 24 24 use Visualwebs\ML\Helper\Chatbot as ChatbotHelper; 25 use Visualwebs\ML\Helper\Pinecone as PineconeHelper;26 use Visualwebs\ML\Helper\OpenAi as OpenAiHelper;27 25 28 26 register_activation_hook( 29 27 plugin_dir_path( __DIR__ ) . 'visualwebs-ml.php', 30 28 function () { 31 if ( ! wp_next_scheduled( 'visualwebs_ml_semantic_queue_processor' ) ) { 32 wp_schedule_event( time(), 'every_five_minutes', 'visualwebs_ml_semantic_queue_processor' ); 33 } 34 if ( ! wp_next_scheduled( 'visualwebs_ml_daily_dynamic_pricing' ) ) { 35 $next_run = strtotime( 'tomorrow 07:00:00' ); 36 wp_schedule_event( $next_run, 'daily', 'visualwebs_ml_daily_dynamic_pricing' ); 37 } 38 if ( ! wp_next_scheduled( 'visualwebs_ml_check_dynamic_pricing_batch_jobs' ) ) { 39 wp_schedule_event( time(), 'every_five_minutes', 'visualwebs_ml_check_dynamic_pricing_batch_jobs' ); 40 } 41 if ( ! wp_next_scheduled( 'visualwebs_ml_weekly_dynamic_pricing_training' ) ) { 42 $next_run = strtotime( 'tomorrow 00:00:00' ); 43 wp_schedule_event( $next_run, 'weekly', 'visualwebs_ml_weekly_dynamic_pricing_training' ); 29 // Feed generation cron jobs 30 if ( ! wp_next_scheduled( 'visualwebs_ml_product_feed' ) ) { 31 wp_schedule_event( time(), 'hourly', 'visualwebs_ml_product_feed' ); 32 } 33 if ( ! wp_next_scheduled( 'visualwebs_ml_page_feed' ) ) { 34 $next_run = strtotime( 'tomorrow 02:00:00' ); 35 wp_schedule_event( $next_run, 'daily', 'visualwebs_ml_page_feed' ); 36 } 37 if ( ! wp_next_scheduled( 'visualwebs_ml_sales_feed' ) ) { 38 $next_run = strtotime( 'tomorrow 02:00:00' ); 39 wp_schedule_event( $next_run, 'daily', 'visualwebs_ml_sales_feed' ); 40 } 41 if ( ! wp_next_scheduled( 'visualwebs_ml_insights_feed' ) ) { 42 $next_run = strtotime( 'tomorrow 03:00:00' ); 43 wp_schedule_event( $next_run, 'daily', 'visualwebs_ml_insights_feed' ); 44 44 } 45 45 } … … 49 49 plugin_dir_path( __DIR__ ) . 'visualwebs-ml.php', 50 50 function () { 51 wp_clear_scheduled_hook( 'visualwebs_ml_semantic_queue_processor' ); 52 wp_clear_scheduled_hook( 'visualwebs_ml_daily_dynamic_pricing' ); 53 wp_clear_scheduled_hook( 'visualwebs_ml_check_dynamic_pricing_batch_jobs' ); 54 wp_clear_scheduled_hook( 'visualwebs_ml_weekly_dynamic_pricing_training' ); 55 } 56 ); 57 58 add_filter( 59 'cron_schedules', // phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval 60 function ( $schedules ) { 61 $schedules['every_five_minutes'] = array( 62 'interval' => 300, 63 'display' => __( 'Every 5 Minutes', 'visualwebs-ml' ), 64 ); 65 return $schedules; 66 } 67 ); 68 69 add_action( 70 'visualwebs_ml_semantic_queue_processor', 71 function () { 72 global $wpdb; 73 74 $table_name = $wpdb->prefix . 'visualwebs_ml_semantic_queue'; 75 $batch_limit = 5; 76 77 // Clear completed and disabled jobs. 78 // phpcs:ignore WordPress.DB.DirectDatabaseQuery 79 $wpdb->query( 80 $wpdb->prepare( 81 'DELETE FROM %i 82 WHERE job_status = %d AND job_sync_status = %d', 83 $table_name, 84 0, 85 1 86 ) 87 ); 88 89 // Fetch jobs that are not completed. 90 // phpcs:ignore WordPress.DB.DirectDatabaseQuery 91 $jobs = $wpdb->get_results( 92 $wpdb->prepare( 93 'SELECT * FROM %i 94 WHERE job_sync_status != %d 95 LIMIT %d', 96 $table_name, 97 1, 98 $batch_limit 99 ) 100 ); 101 102 foreach ( $jobs as $job ) { 103 // Process each job. 104 visualwebs_ml_process_queue_item( $job ); 105 106 // Mark the job as completed. 107 // phpcs:ignore WordPress.DB.DirectDatabaseQuery 108 $wpdb->update( 109 $table_name, 110 array( 'job_sync_status' => 1 ), // Mark as completed. 111 array( 'job_id' => $job->job_id ) 112 ); 113 } 114 } 115 ); 116 117 add_action( 118 'visualwebs_ml_daily_dynamic_pricing', 119 'visualwebs_ml_dynamic_pricing' 51 wp_clear_scheduled_hook( 'visualwebs_ml_product_feed' ); 52 wp_clear_scheduled_hook( 'visualwebs_ml_page_feed' ); 53 wp_clear_scheduled_hook( 'visualwebs_ml_sales_feed' ); 54 wp_clear_scheduled_hook( 'visualwebs_ml_insights_feed' ); 55 } 120 56 ); 121 57 … … 250 186 251 187 add_action( 252 'visualwebs_ml_weekly_dynamic_pricing_training',253 function () {254 do_action( 'visualwebs_ml_train_dynamic_pricing' );255 }256 );257 258 add_action(259 188 'visualwebs_ml_train_dynamic_pricing', 260 189 'visualwebs_ml_train_dynamic_pricing' … … 382 311 383 312 384 add_action( 385 'visualwebs_ml_check_dynamic_pricing_batch_jobs', 386 function () { 387 global $wpdb; 388 $helper = new \Visualwebs\ML\Helper\Data(); 389 $api_helper = new ApiHelper(); 390 391 if ( ! $helper->getIsDynamicPricingEnabled() ) { 392 return false; 393 } 394 // Fetch all pending jobs. 395 $table_name = $wpdb->prefix . 'visualwebs_ml_dynamic_pricing_jobs'; 396 // phpcs:ignore WordPress.DB.DirectDatabaseQuery, phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching 397 $pending_jobs = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE job_status = %s', $table_name, 'processing' ) ); 398 foreach ( $pending_jobs as $job ) { 399 $job_id = $job->job_id; 400 $job_hash = $job->job_hash; 401 $data = array( 402 'job_hash' => $job_hash, 403 ); 404 405 $response = $api_helper->sendDynamicPricingBatchJobStatusRequest( $data ); 406 407 if ( 'done' === $response['status'] && ! empty( $response['job_id'] ) ) { 408 409 if ( 'predict' === $response['type'] ) { 313 function visualwebs_ml_check_dynamic_pricing_batch_jobs() { 314 global $wpdb; 315 $helper = new \Visualwebs\ML\Helper\Data(); 316 $api_helper = new ApiHelper(); 317 318 if ( ! $helper->getIsDynamicPricingEnabled() ) { 319 return false; 320 } 321 // Fetch all pending jobs. 322 $table_name = $wpdb->prefix . 'visualwebs_ml_dynamic_pricing_jobs'; 323 // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 324 $pending_jobs = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE job_status = %s', $table_name, 'processing' ) ); 325 foreach ( $pending_jobs as $job ) { 326 $job_id = $job->job_id; 327 $job_hash = $job->job_hash; 328 $data = array( 329 'job_hash' => $job_hash, 330 ); 331 332 $response = $api_helper->sendDynamicPricingBatchJobStatusRequest( $data ); 333 334 if ( 'done' === $response['status'] && ! empty( $response['job_id'] ) ) { 335 336 if ( 'predict' === $response['type'] ) { 410 337 411 338 $results = $api_helper->downloadDynamicPricingBatchResults( $response['job_id'] ); … … 439 366 440 367 if ( $new_price <= $cogs ) { 441 $min_pct = min( 1, $helper->getDynamicPricingMinProfit() ); 368 // Default minimum profit 10% (config for min_profit was removed, now managed in SaaS) 369 $min_pct = 10; 442 370 $new_price = $cogs * ( 1 + ( $min_pct / 100 ) ); 443 371 } … … 487 415 } 488 416 } 489 );490 417 491 418 /** … … 500 427 function visualwebs_ml_process_queue_item( $job ) { 501 428 global $wpdb; 502 $helper = new Helper(); 503 $pinecone_helper = new PineconeHelper(); 504 $opean_ai_helper = new OpenAiHelper(); 505 $api_helper = new ApiHelper(); 506 $chatbot_helper = new ChatbotHelper(); 507 508 $use_customer_api_keys_enabled = $helper->getUseCustomerApiKeysEnabled(); 509 $pinecone_api_key = $pinecone_helper->getPineconeApiKey(); 510 $pinecone_index_host = $pinecone_helper->getPineconeIndexHost(); 511 $pinecone_namespace = $pinecone_helper->getPineconeNamespace(); 512 $openai_api_key = $opean_ai_helper->getApiKey(); 513 $domain_namespace = $helper->getDomainNamespace(); 514 515 $params = $use_customer_api_keys_enabled ? array( 516 'openai_api_key' => $openai_api_key, 517 'pinecone_api_key' => $pinecone_api_key, 518 'pinecone_index_host' => $pinecone_index_host, 519 'pinecone_namespace' => $pinecone_namespace, 520 ) : array( 521 'pinecone_namespace' => $domain_namespace, 429 $helper = new Helper(); 430 $api_helper = new ApiHelper(); 431 $chatbot_helper = new ChatbotHelper(); 432 433 // Legacy semantic search processing (now managed in SaaS) 434 $params = array( 435 'pinecone_namespace' => $helper->getDomainNamespace(), 522 436 ); 523 437 … … 675 589 ); 676 590 } 591 592 // Feed generation cron job handlers 593 594 add_action('visualwebs_ml_product_feed', function() { 595 require_once plugin_dir_path(__FILE__) . 'feeds/class-product-feed-generator.php'; 596 $generator = new VisualwebsML_ProductFeedGenerator(); 597 $generator->generate(); 598 }); 599 600 add_action('visualwebs_ml_page_feed', function() { 601 require_once plugin_dir_path(__FILE__) . 'feeds/class-page-feed-generator.php'; 602 $generator = new VisualwebsML_PageFeedGenerator(); 603 $generator->generate(); 604 }); 605 606 add_action('visualwebs_ml_sales_feed', function() { 607 require_once plugin_dir_path(__FILE__) . 'feeds/class-sales-feed-generator.php'; 608 $generator = new VisualwebsML_SalesFeedGenerator(); 609 $generator->generate(); 610 }); 611 612 add_action('visualwebs_ml_insights_feed', function() { 613 require_once plugin_dir_path(__FILE__) . 'feeds/class-insights-feed-generator.php'; 614 $generator = new VisualwebsML_InsightsFeedGenerator(); 615 $generator->generate(); 616 }); -
visualwebs-ml/tags/5.5.0/includes/redux-config.php
r3325171 r3476789 23 23 } 24 24 25 $ opt_name = 'visualwebs_ml_options';26 27 $ args = array(28 'opt_name' => $ opt_name,25 $visualwebs_ml_opt_name = 'visualwebs_ml_options'; 26 27 $visualwebs_ml_args = array( 28 'opt_name' => $visualwebs_ml_opt_name, 29 29 'display_name' => 'Visualwebs AI Cloud Suite Options', 30 30 'menu_type' => 'submenu', … … 57 57 ); 58 58 59 Redux::setArgs( $ opt_name, $args );59 Redux::setArgs( $visualwebs_ml_opt_name, $visualwebs_ml_args ); 60 60 61 61 // Extension Options Group. 62 62 Redux::setSection( 63 $ opt_name,63 $visualwebs_ml_opt_name, 64 64 array( 65 65 'title' => __( 'Extension Options', 'visualwebs-ml' ), … … 82 82 // Visualwebs AI Cloud Suite API Group. 83 83 Redux::setSection( 84 $ opt_name,84 $visualwebs_ml_opt_name, 85 85 array( 86 86 'title' => __( 'API', 'visualwebs-ml' ), … … 107 107 ), 108 108 array( 109 'id' => 'use_customer_api_keys', 110 'type' => 'switch', 111 'title' => __( 'Use your own API Keys', 'visualwebs-ml' ), 112 'default' => false, 113 'on' => 'Enabled', 114 'off' => 'Disabled', 115 'desc' => __( 'Enable only if you want to use your OpenAI and Pinecone keys.', 'visualwebs-ml' ), 116 ), 117 ), 118 ) 119 ); 120 121 // User Widgets Group. 122 Redux::setSection( 123 $opt_name, 124 array( 125 'title' => __( 'User Widgets', 'visualwebs-ml' ), 126 'id' => 'widgets', 127 'desc' => __( 'Settings for User Widgets.', 'visualwebs-ml' ), 128 'icon' => 'el el-th-large', 129 'fields' => array( 130 array( 131 'id' => 'user_widgets', 132 'type' => 'repeater', 133 'group_values' => true, 134 'title' => __( 'Widgets', 'visualwebs-ml' ), 135 'desc' => __( 'Configure user widgets.', 'visualwebs-ml' ), 136 'fields' => array( 137 array( 138 'id' => 'widget_title', 139 'type' => 'text', 140 'title' => __( 'Title', 'visualwebs-ml' ), 141 'default' => '', 142 ), 143 array( 144 'id' => 'widget_instructions', 145 'type' => 'text', 146 'title' => __( 'Instructions', 'visualwebs-ml' ), 147 'default' => '', 148 ), 149 array( 150 'id' => 'widget_render_el', 151 'type' => 'text', 152 'title' => __( 'Render Element', 'visualwebs-ml' ), 153 'default' => '', 154 ), 155 array( 156 'id' => 'widget_source_el', 157 'type' => 'text', 158 'title' => __( 'Source Element', 'visualwebs-ml' ), 159 'default' => '', 160 ), 109 'id' => 'store_id', 110 'type' => 'text', 111 'title' => __( 'Store ID (SaaS UUID)', 'visualwebs-ml' ), 112 'desc' => __( 'UUID from SaaS Dashboard when registering this store.', 'visualwebs-ml' ), 113 'required' => array( 'api_enabled', '=', '1' ), 114 'validate' => 'no_html', 115 ), 116 ), 117 ) 118 ); 119 120 // Workflow Integration Group. 121 Redux::setSection( 122 $visualwebs_ml_opt_name, 123 array( 124 'title' => __( 'Workflow Integration', 'visualwebs-ml' ), 125 'id' => 'workflow', 126 'desc' => __( 'Configure event delivery to SaaS and n8n workflows.', 'visualwebs-ml' ), 127 'icon' => 'el el-random', 128 'fields' => array( 129 array( 130 'id' => 'enable_workflows', 131 'type' => 'switch', 132 'title' => __( 'Enable Workflows', 'visualwebs-ml' ), 133 'default' => false, 134 'on' => 'Enabled', 135 'off' => 'Disabled', 136 'required' => array( 'api_enabled', '=', '1' ), 137 ), 138 array( 139 'id' => 'enabled_events', 140 'type' => 'checkbox', 141 'title' => __( 'Enabled Events', 'visualwebs-ml' ), 142 'options' => array( 143 'woocommerce_new_order' => __( 'Order Created', 'visualwebs-ml' ), 144 'woocommerce_order_status_changed' => __( 'Order Status Changed', 'visualwebs-ml' ), 145 'user_register' => __( 'User Registered', 'visualwebs-ml' ), 146 'woocommerce_product_updated' => __( 'Product Updated', 'visualwebs-ml' ), 161 147 ), 162 'default' => array(), 148 'default' => array( 149 'woocommerce_new_order' => '1', 150 'woocommerce_order_status_changed' => '1', 151 ), 152 'required' => array( 'enable_workflows', '=', '1' ), 153 ), 154 array( 155 'id' => 'anonymize_payload', 156 'type' => 'switch', 157 'title' => __( 'Anonymize Personal Data (GDPR)', 'visualwebs-ml' ), 158 'default' => true, 159 'on' => 'Enabled', 160 'off' => 'Disabled', 161 'desc' => __( 'Redacts personal fields before sending event payloads.', 'visualwebs-ml' ), 162 'required' => array( 'enable_workflows', '=', '1' ), 163 163 ), 164 164 ), … … 168 168 // ChatGPT Group. 169 169 Redux::setSection( 170 $ opt_name,170 $visualwebs_ml_opt_name, 171 171 array( 172 172 'title' => __( 'ChatGPT', 'visualwebs-ml' ), … … 176 176 'fields' => array( 177 177 array( 178 'id' => 'chatgpt_api_key',179 'type' => 'text',180 'title' => __( 'Open AI Api key', 'visualwebs-ml' ),181 'required' => array( 'use_customer_api_keys', '=', '1' ),182 ),183 array(184 178 'id' => 'chatbot_enabled', 185 179 'type' => 'switch', … … 191 185 ), 192 186 array( 193 'id' => 'debug_enabled', 194 'type' => 'switch', 195 'title' => __( 'Enables the Chatbot debug', 'visualwebs-ml' ), 196 'default' => false, 197 'on' => 'Enabled', 198 'off' => 'Disabled', 199 'desc' => __( 'Writes Chatbot information in logs/visualwebs_ml.log', 'visualwebs-ml' ), 200 ), 201 array( 202 'id' => 'chatbot_header_title', 203 'type' => 'text', 204 'default' => 'Support', 205 'title' => __( 'Chatbot header', 'visualwebs-ml' ), 206 'desc' => __( 'The title to show in the header of chat widget, i.e. Support, mysite.com Support..', 'visualwebs-ml' ), 207 ), 208 array( 209 'id' => 'chatbot_logo', 210 'type' => 'media', 211 'title' => __( 'Upload Logo', 'visualwebs-ml' ), 212 'desc' => __( 'Allowed file types: jpg, jpeg, gif, png', 'visualwebs-ml' ), 213 ), 214 array( 215 'id' => 'chatbot_main_background_color', 216 'type' => 'color', 217 'title' => __( 'Color', 'visualwebs-ml' ), 218 'default' => '#ffffff', 219 'required' => array( 'chatbot_enabled', '=', '1' ), 220 'validate' => 'not_empty', 221 ), 222 array( 223 'id' => 'chatbot_context', 224 'type' => 'textarea', 225 'default' => 'Act as E-commerce support team', 226 'title' => __( 'Chatbot context', 'visualwebs-ml' ), 227 'desc' => __( 'Provide clear instructions for the chatbot\'s behavior.', 'visualwebs-ml' ), 228 'required' => array( 'chatbot_enabled', '=', '1' ), 229 'validate' => 'not_empty', 230 ), 231 array( 232 'id' => 'chatbot_offline_message', 233 'type' => 'text', 234 'default' => 'Our support system is OFF now, please try later.', 235 'title' => __( 'Message to show to customers when Bot is Offline.', 'visualwebs-ml' ), 236 'desc' => __( 'Example: Our support system is OFF now, please try later.', 'visualwebs-ml' ), 237 ), 238 array( 239 'id' => 'chatbot_disclaimer_url', 240 'type' => 'text', 241 'title' => __( 'Disclaimer url', 'visualwebs-ml' ), 242 'desc' => __( 'Specify the slug of the disclaimer page.', 'visualwebs-ml' ), 243 ), 244 ), 245 ) 246 ); 247 248 // Semantic Search Group. 249 Redux::setSection( 250 $opt_name, 251 array( 252 'title' => __( 'Semantic Search', 'visualwebs-ml' ), 253 'id' => 'semantic_search', 254 'desc' => __( 'Settings for Semantic Search.', 'visualwebs-ml' ), 255 'icon' => 'el el-search', 256 'fields' => array( 257 array( 258 'id' => 'semantic_search_enabled', 259 'type' => 'switch', 260 'title' => __( 'Enables semantic search', 'visualwebs-ml' ), 261 'default' => true, 262 'on' => 'Enabled', 263 'off' => 'Disabled', 264 'desc' => __( 'Enables the Semantic search. Requires a vector database connection.', 'visualwebs-ml' ), 265 ), 266 array( 267 'id' => 'vector_database_service', 268 'type' => 'text', 269 'title' => __( 'Vector database service', 'visualwebs-ml' ), 270 'required' => array( 'use_customer_api_keys', '=', '1' ), 271 'desc' => __( 'Use a free Pinacone account as starting point. Note you would need a paid subscription for large websites.', 'visualwebs-ml' ), 272 ), 273 array( 274 'id' => 'vector_database_api_key', 275 'type' => 'text', 276 'title' => __( 'Vector database Api key', 'visualwebs-ml' ), 277 'required' => array( 'use_customer_api_keys', '=', '1' ), 278 ), 279 array( 280 'id' => 'vector_database_index_host', 281 'type' => 'text', 282 'title' => __( 'Vector database index host', 'visualwebs-ml' ), 283 'required' => array( 'use_customer_api_keys', '=', '1' ), 284 'desc' => __( 'Find it in Pinecone panel, select the desired index and copy the host.', 'visualwebs-ml' ), 285 ), 286 array( 287 'id' => 'vector_index_namespace', 288 'type' => 'text', 289 'title' => __( 'Vector index namespace', 'visualwebs-ml' ), 290 'required' => array( 'use_customer_api_keys', '=', '1' ), 291 'desc' => __( 'Optional for paid vector accounts. Leave empty as general rule.', 'visualwebs-ml' ), 292 ), 293 array( 294 'id' => 'vector_min_search_score', 295 'type' => 'text', 296 'title' => __( 'Vector min search score', 'visualwebs-ml' ), 297 'default' => '0.75', 298 'desc' => __( 'Start using 0.75. Use lower values to use vector data more frequently.', 'visualwebs-ml' ), 299 ), 300 array( 301 'id' => 'vector_store_dynamic_data', 302 'type' => 'switch', 303 'title' => __( 'Store Product Price and Stock Data', 'visualwebs-ml' ), 304 'default' => false, 305 'on' => 'Enabled', 306 'off' => 'Disabled', 307 'desc' => __( 'Enable if your catalog doesn\'t change frequently. This will add product price and stock data to chatbot knowledge.', 'visualwebs-ml' ), 187 'id' => 'admin_enabled', 188 'type' => 'switch', 189 'title' => __( 'Enable admin widget', 'visualwebs-ml' ), 190 'default' => false, 191 'on' => 'Enabled', 192 'off' => 'Disabled', 193 'desc' => __( 'Enable chatbot widget on admin screens.', 'visualwebs-ml' ), 308 194 ), 309 195 ), … … 313 199 // Dynamic Pricing Group. 314 200 Redux::setSection( 315 $ opt_name,201 $visualwebs_ml_opt_name, 316 202 array( 317 203 'title' => __( 'SmartPricing AI', 'visualwebs-ml' ), … … 344 230 'desc' => __( 'Automatically replaces catalog prices with daily AI predictions.', 'visualwebs-ml' ), 345 231 ), 346 array( 347 'id' => 'dynamic_pricing_min_profit', 348 'type' => 'text', 349 'title' => __( 'Minimum Profit (%)', 'visualwebs-ml' ), 350 'default' => '10', 351 'desc' => __( 'Minimum profit margin as a percentage.', 'visualwebs-ml' ), 352 ), 353 array( 354 'id' => 'dynamic_pricing_max_profit', 355 'type' => 'text', 356 'title' => __( 'Maximum Profit (%)', 'visualwebs-ml' ), 357 'default' => '35', 358 'desc' => __( 'Maximum profit margin as a percentage.', 'visualwebs-ml' ), 359 ), 360 ), 361 ) 362 ); 232 ), 233 ) 234 ); -
visualwebs-ml/tags/5.5.0/readme.txt
r3357523 r3476789 3 3 Tags: AI, Chatbot, Machine Learning, Spam, ChatGPT 4 4 Requires at least: 6.2 5 Tested up to: 6. 85 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 5. 4.37 Stable tag: 5.5.0 8 8 License: GPLv2 or later 9 9 License URI: https://visualwebs.eu/terms-and-conditions/ … … 80 80 81 81 == Changelog == 82 = 5.2.0 = 83 * Initial release. 84 = 5.3.0 = 85 * Removed the minimum data requirement for Spam and Sentiment Analysis widgets. These can now display results even with just one review, without needing to train a model. 82 = 5.5.0 = 83 * Major Update: Migrated to SaaS architecture (matches Magento module functionality) 84 * New: n8n workflow integration with event system (order events, customer events, product events) 85 * New: Product feed generator with hourly cron job for semantic search sync 86 * New: Page feed generator with daily cron job 87 * New: Sales feed generator for SmartPricing AI training data 88 * New: Insights feed generator (`insights_feed.json`) for dashboard widgets 89 * New: Manual feed generation buttons in admin panel 90 * New: Chatbot admin/backend integration with secure admin endpoint (capability + nonce) 91 * Security: GDPR-compliant payload anonymization for workflows 92 * Improved: Simplified admin interface (dashboard + settings + feed generation) 93 * Deprecated: Database table access methods (use SaaS API instead) 94 * Deprecated: Semantic search and dynamic pricing queue grid classes (use SaaS dashboard) 95 * Performance: Removed legacy cron jobs and unused local processing paths 96 97 = 5.4.3 = 98 * Added English/Spanish translation support for widgets and dashboard. 99 = 5.4.2 = 100 * Improved: Simplified chatbot security to ensure compatibility with caching plugins and language translators. Security checks are now handled by our remote service, which uses a license-based rate limiter for protection. 101 = 5.4.1 = 102 * New feature: Track SmartPricing AI training results together with prediction jobs for improved monitoring and transparency. 86 103 = 5.4.0 = 87 104 * New feature: AI-powered dynamic pricing to help you set the best product sale price and maximize WooCommerce profits. 88 105 * New feature: Bulk append products, categories, or pages for faster AI processing and management. 89 106 * New feature: Attach PDF, DOC, or text files to each semantic search item. Uploaded files are added to the chatbot's knowledge base. 90 = 5.4.1 = 91 * New feature: Track SmartPricing AI training results together with prediction jobs for improved monitoring and transparency. 92 = 5.4.2 = 93 * Improved: Simplified chatbot security to ensure compatibility with caching plugins and language translators. Security checks are now handled by our remote service, which uses a license-based rate limiter for protection. 94 = 5.4.3 = 95 * Added English/Spanish translation support for widgets and dashboard. 107 = 5.3.0 = 108 * Removed the minimum data requirement for Spam and Sentiment Analysis widgets. These can now display results even with just one review, without needing to train a model. 109 = 5.2.0 = 110 * Initial release. 96 111 97 112 == License == -
visualwebs-ml/tags/5.5.0/templates/ml-dashboard.php
r3319473 r3476789 9 9 * @copyright Visualwebs Spain 10 10 * @license See https://visualwebs.eu/terms-and-conditions/ for license details. 11 * 12 * @since 5.5.0 - Migrated to SaaS architecture 11 13 */ 12 14 … … 19 21 } 20 22 21 use Visualwebs\ML\Block\Adminhtml\Dashboard;22 23 use Visualwebs\ML\Helper\Data as Helper; 23 24 24 $ dashboard = new Dashboard();25 $visualwebs_ml_helper = new Helper(); 25 26 26 if ( ! $ dashboard->getIsVisualwebsMlServicesEnabled() ) {27 if ( ! $visualwebs_ml_helper->getIsVisualwebsMlServicesEnabled() ) { 27 28 echo '<div class="notice notice-warning"><p>'; 28 29 echo esc_html__( 'Please purchase a subscription ', 'visualwebs-ml' ); … … 35 36 } 36 37 37 if ( isset( $_GET['clear_cache'] ) ) { 38 check_admin_referer( 'clear_cache_action' ); 39 if ( true === (bool) $_GET['clear_cache'] ) { 40 delete_transient( Helper::CACHE_TRANSIENT ); 41 } 42 } 38 $visualwebs_ml_dashboard_url = 'https://saas.visualwebs.eu/dashboard'; 39 $visualwebs_ml_api_key = $visualwebs_ml_helper->getApiKey(); 40 $visualwebs_ml_store_id = $visualwebs_ml_helper->getStoreId(); 41 $visualwebs_ml_workflows_enabled = $visualwebs_ml_helper->getConfig('enable_workflows', false); 42 ?> 43 44 <div class="wrap"> 45 <h1><?php esc_html_e('Visualwebs AI Cloud Suite', 'visualwebs-ml'); ?></h1> 46 47 <div class="card"> 48 <h2><?php esc_html_e('SaaS Dashboard', 'visualwebs-ml'); ?></h2> 49 <p><?php esc_html_e('All AI features are managed from the centralized SaaS dashboard:', 'visualwebs-ml'); ?></p> 50 <ul> 51 <li><?php esc_html_e('Semantic Search Queue Management', 'visualwebs-ml'); ?></li> 52 <li><?php esc_html_e('SmartPricing AI Jobs & Training', 'visualwebs-ml'); ?></li> 53 <li><?php esc_html_e('n8n Workflow Monitoring', 'visualwebs-ml'); ?></li> 54 <li><?php esc_html_e('Analytics & Insights', 'visualwebs-ml'); ?></li> 55 </ul> 56 <p> 57 <a href="<?php echo esc_url($visualwebs_ml_dashboard_url); ?>" target="_blank" class="button button-primary button-hero"> 58 <?php esc_html_e('Open SaaS Dashboard', 'visualwebs-ml'); ?> → 59 </a> 60 </p> 61 </div> 62 63 <div class="card"> 64 <h2><?php esc_html_e('Configuration Status', 'visualwebs-ml'); ?></h2> 65 <table class="widefat"> 66 <tbody> 67 <tr> 68 <td><strong><?php esc_html_e('API Key', 'visualwebs-ml'); ?></strong></td> 69 <td> 70 <?php if ($visualwebs_ml_api_key): ?> 71 <span style="color: #46b450;">✓ <?php esc_html_e('Configured', 'visualwebs-ml'); ?></span> 72 <?php else: ?> 73 <span style="color: #dc3232;">✗ <?php esc_html_e('Missing', 'visualwebs-ml'); ?></span> 74 <?php endif; ?> 75 </td> 76 </tr> 77 <tr> 78 <td><strong><?php esc_html_e('Store ID', 'visualwebs-ml'); ?></strong></td> 79 <td> 80 <?php if ($visualwebs_ml_store_id): ?> 81 <span style="color: #46b450;">✓ <?php echo esc_html($visualwebs_ml_store_id); ?></span> 82 <?php else: ?> 83 <span style="color: #dc3232;">✗ <?php esc_html_e('Missing', 'visualwebs-ml'); ?></span> 84 <?php endif; ?> 85 </td> 86 </tr> 87 <tr> 88 <td><strong><?php esc_html_e('n8n Workflows', 'visualwebs-ml'); ?></strong></td> 89 <td> 90 <?php if ($visualwebs_ml_workflows_enabled): ?> 91 <span style="color: #46b450;">✓ <?php esc_html_e('Enabled', 'visualwebs-ml'); ?></span> 92 <?php else: ?> 93 <span style="color: #999;">✗ <?php esc_html_e('Disabled', 'visualwebs-ml'); ?></span> 94 <?php endif; ?> 95 </td> 96 </tr> 97 <tr> 98 <td><strong><?php esc_html_e('SmartPricing AI', 'visualwebs-ml'); ?></strong></td> 99 <td> 100 <?php if ($visualwebs_ml_helper->getIsDynamicPricingEnabled()): ?> 101 <span style="color: #46b450;">✓ <?php esc_html_e('Enabled', 'visualwebs-ml'); ?></span> 102 <?php else: ?> 103 <span style="color: #999;">✗ <?php esc_html_e('Disabled', 'visualwebs-ml'); ?></span> 104 <?php endif; ?> 105 </td> 106 </tr> 107 <tr> 108 <td><strong><?php esc_html_e('Chatbot Widget', 'visualwebs-ml'); ?></strong></td> 109 <td> 110 <?php if ($visualwebs_ml_helper->getConfig('chatbot_enabled', false)): ?> 111 <span style="color: #46b450;">✓ <?php esc_html_e('Enabled', 'visualwebs-ml'); ?></span> 112 <?php else: ?> 113 <span style="color: #999;">✗ <?php esc_html_e('Disabled', 'visualwebs-ml'); ?></span> 114 <?php endif; ?> 115 </td> 116 </tr> 117 </tbody> 118 </table> 119 </div> 120 121 <div class="card"> 122 <h2><?php esc_html_e('Feed Synchronization', 'visualwebs-ml'); ?></h2> 123 <p><?php esc_html_e('Automatic feed generation is configured:', 'visualwebs-ml'); ?></p> 124 <ul> 125 <li><strong><?php esc_html_e('Product Feed', 'visualwebs-ml'); ?>:</strong> <?php esc_html_e('Hourly', 'visualwebs-ml'); ?></li> 126 <li><strong><?php esc_html_e('Page Feed', 'visualwebs-ml'); ?>:</strong> <?php esc_html_e('Daily at 2 AM', 'visualwebs-ml'); ?></li> 127 <li><strong><?php esc_html_e('Sales Feed', 'visualwebs-ml'); ?>:</strong> <?php esc_html_e('Daily at 2 AM', 'visualwebs-ml'); ?></li> 128 </ul> 129 <p> 130 <a href="<?php echo esc_url(admin_url('admin.php?page=visualwebs-ml-feeds')); ?>" class="button"> 131 <?php esc_html_e('Manage Feeds', 'visualwebs-ml'); ?> 132 </a> 133 </p> 134 </div> 135 136 <div class="card"> 137 <h2><?php esc_html_e('Quick Links', 'visualwebs-ml'); ?></h2> 138 <p> 139 <a href="<?php echo esc_url(admin_url('admin.php?page=visualwebs-ml-manage-options')); ?>" class="button"> 140 <?php esc_html_e('Settings', 'visualwebs-ml'); ?> 141 </a> 142 <a href="<?php echo esc_url(admin_url('admin.php?page=visualwebs-ml-feeds')); ?>" class="button"> 143 <?php esc_html_e('Feed Generation', 'visualwebs-ml'); ?> 144 </a> 145 <a href="https://visualwebs.eu/docs/plugin/ai-cloud-suite/" target="_blank" class="button"> 146 <?php esc_html_e('Documentation', 'visualwebs-ml'); ?> 147 </a> 148 </p> 149 </div> 150 </div> -
visualwebs-ml/tags/5.5.0/vendor/autoload.php
r3303071 r3476789 15 15 } 16 16 } 17 trigger_error( 18 $err, 19 E_USER_ERROR 20 ); 17 throw new RuntimeException($err); 21 18 } 22 19 -
visualwebs-ml/tags/5.5.0/vendor/composer/autoload_static.php
r3319473 r3476789 12 12 13 13 public static $prefixLengthsPsr4 = array ( 14 'V' => 14 'V' => 15 15 array ( 16 16 'Visualwebs\\ML\\' => 14, 17 17 ), 18 'S' => 18 'S' => 19 19 array ( 20 20 'Symfony\\Polyfill\\Mbstring\\' => 26, 21 21 ), 22 'P' => 22 'P' => 23 23 array ( 24 24 'PhpOffice\\PhpWord\\' => 18, … … 28 28 29 29 public static $prefixDirsPsr4 = array ( 30 'Visualwebs\\ML\\' => 30 'Visualwebs\\ML\\' => 31 31 array ( 32 32 0 => __DIR__ . '/..' . '/visualwebs-ml', 33 33 ), 34 'Symfony\\Polyfill\\Mbstring\\' => 34 'Symfony\\Polyfill\\Mbstring\\' => 35 35 array ( 36 36 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', 37 37 ), 38 'PhpOffice\\PhpWord\\' => 38 'PhpOffice\\PhpWord\\' => 39 39 array ( 40 40 0 => __DIR__ . '/..' . '/phpoffice/phpword/src/PhpWord', 41 41 ), 42 'PhpOffice\\Math\\' => 42 'PhpOffice\\Math\\' => 43 43 array ( 44 44 0 => __DIR__ . '/..' . '/phpoffice/math/src/Math', … … 47 47 48 48 public static $prefixesPsr0 = array ( 49 'S' => 49 'S' => 50 50 array ( 51 'Smalot\\PdfParser\\' => 51 'Smalot\\PdfParser\\' => 52 52 array ( 53 53 0 => __DIR__ . '/..' . '/smalot/pdfparser/src', -
visualwebs-ml/tags/5.5.0/vendor/composer/platform_check.php
r3319484 r3476789 20 20 } 21 21 } 22 trigger_error( 23 'Composer detected issues in your platform: ' . implode(' ', $issues), 24 E_USER_ERROR 22 throw new \RuntimeException( 23 'Composer detected issues in your platform: ' . implode(' ', $issues) 25 24 ); 26 25 } -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Activator.php
r3325281 r3476789 3 3 namespace Visualwebs\ML; 4 4 5 /** 6 * Plugin Activator 7 * 8 * @package Visualwebs\ML 9 * @since 5.5.0 10 * 11 * Note: Database tables removed in v5.5.0 - SaaS architecture migration. 12 * All data is now managed centrally by the SaaS platform. 13 * Configuration is stored in wp_options table. 14 */ 5 15 class Activator 6 16 { 17 /** 18 * Plugin activation hook 19 * 20 * @since 5.5.0 No database tables created - SaaS manages all data 21 */ 7 22 public static function activate() 8 23 { 9 global $wpdb; 10 11 $sematic_queue_table_name = $wpdb->prefix . 'visualwebs_ml_semantic_queue'; 12 $charset_collate = $wpdb->get_charset_collate(); 13 14 $sql1 = "CREATE TABLE $sematic_queue_table_name ( 15 job_id int(11) NOT NULL AUTO_INCREMENT COMMENT 'Job Id', 16 job_object_entity_id int(11) NOT NULL COMMENT 'Job Object Entity Id', 17 job_object_type varchar(50) NOT NULL COMMENT 'Job Object Type', 18 job_object_name varchar(255) DEFAULT NULL COMMENT 'Job Object Name', 19 job_object_ref varchar(255) DEFAULT NULL COMMENT 'Job Object Ref', 20 job_object_content text COMMENT 'Job Object Content', 21 job_object_content_lock smallint(6) NOT NULL DEFAULT '0' COMMENT 'Job Object Content Lock', 22 job_status smallint(6) NOT NULL DEFAULT '0' COMMENT 'Status', 23 job_sync_status smallint(6) NOT NULL DEFAULT '0' COMMENT 'Sync Status', 24 created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Date and time of job creation', 25 updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Date and time of job update', 26 job_errors varchar(255) DEFAULT NULL COMMENT 'Job Errors', 27 job_object_files_content text COMMENT 'Job Object Files Content', 28 PRIMARY KEY (job_id), 29 UNIQUE KEY ML_SEMANTIC_QUEUE_JOB_OBJECT_TYPE_JOB_OBJECT_ENTITY_ID (job_object_type, job_object_entity_id) 30 ) $charset_collate;"; 31 32 $dynamic_pricing_jobs_table_name = $wpdb->prefix . 'visualwebs_ml_dynamic_pricing_jobs'; 33 34 $sql2 = "CREATE TABLE $dynamic_pricing_jobs_table_name ( 35 job_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, 36 job_hash VARCHAR(128) NOT NULL, 37 job_type VARCHAR(32) NOT NULL DEFAULT 'train', 38 job_status VARCHAR(20) NOT NULL DEFAULT 'pending', 39 created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Date and time of job creation', 40 updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Date and time of job update', 41 job_data LONGTEXT, 42 job_response LONGTEXT, 43 PRIMARY KEY (job_id), 44 UNIQUE KEY job_hash (job_hash) 45 ) $charset_collate;"; 46 47 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 48 dbDelta($sql1); 49 dbDelta($sql2); 24 // No database tables needed - SaaS architecture 25 // Configuration is stored in wp_options via Redux Framework 26 // Feeds are generated as JSON files in wp-content/uploads/visualwebs-ai/ 27 28 // Ensure upload directory exists 29 $upload_dir = wp_upload_dir(); 30 $visualwebs_dir = $upload_dir['basedir'] . '/visualwebs-ai'; 31 32 if (!file_exists($visualwebs_dir)) { 33 wp_mkdir_p($visualwebs_dir); 34 } 35 36 // Set default options if not present 37 $options = get_option('visualwebs_ml_options'); 38 39 if ($options === false) { 40 $default_options = array( 41 'enabled' => false, 42 'api_enabled' => false, 43 'chatbot_enabled' => false, 44 'enable_workflows' => false, 45 'anonymize_payload' => false, 46 ); 47 48 add_option('visualwebs_ml_options', $default_options); 49 } 50 51 // Flush rewrite rules for REST API endpoints 52 flush_rewrite_rules(); 50 53 } 51 54 } -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Admin.php
r3319473 r3476789 25 25 { 26 26 add_action('admin_menu', [__CLASS__, 'add_admin_menu']); 27 add_action('admin_footer', [__CLASS__, ' widget_page']);27 add_action('admin_footer', [__CLASS__, 'render_chatbot_widget']); 28 28 add_action('wp_ajax_visualwebs_ml_search_entity', [__CLASS__, 'visualwebs_ml_search_entity']); 29 29 if (self::get_helper()->getIsDynamicPricingEnabled()) { … … 62 62 add_submenu_page( 63 63 'visualwebs-ml', 64 'Semantic Search',65 'Semantic Search',66 'manage_options',67 'visualwebs-ml-semantic-queue',68 [__CLASS__, 'semantic_search_queue_page']69 );70 71 add_submenu_page(72 null,73 'Add New Search Item',74 'Add New Search Item',75 'manage_options',76 'visualwebs-ml-semantic-add',77 [__CLASS__, 'semantic_search_add_page']78 );79 80 add_submenu_page(81 null,82 'Edit Search Item',83 'Edit Search Item',84 'manage_options',85 'visualwebs-ml-semantic-edit',86 [__CLASS__, 'semantic_search_edit_page']87 );88 89 add_submenu_page(90 'visualwebs-ml',91 'SmartPricing AI',92 'SmartPricing AI',93 'manage_options',94 'visualwebs-ml-dynamic-pricing-queue',95 [__CLASS__, 'dynamic_pricing_queue_page']96 );97 98 add_submenu_page(99 null,100 'View Job Item',101 'View Job Item',102 'manage_options',103 'visualwebs-ml-dynamic-pricing-view',104 [__CLASS__, 'dynamic_pricing_queue_view_page']105 );106 107 add_submenu_page(108 null,109 'Train',110 'Train',111 'manage_options',112 'visualwebs-ml-dynamic-pricing-train',113 [__CLASS__, 'dynamic_pricing_train_page']114 );115 116 add_submenu_page(117 null,118 'Predict',119 'Predict',120 'manage_options',121 'visualwebs-ml-dynamic-pricing-predict',122 [__CLASS__, 'dynamic_pricing_predict_page']123 );124 125 add_submenu_page(126 'visualwebs-ml',127 64 'Settings', 128 65 'Settings', … … 130 67 'visualwebs-ml-manage-options', 131 68 [__CLASS__, 'settings_page'] 69 ); 70 71 add_submenu_page( 72 'visualwebs-ml', 73 'Feed Generation', 74 'Feed Generation', 75 'manage_options', 76 'visualwebs-ml-feeds', 77 [__CLASS__, 'feeds_page'] 132 78 ); 133 79 … … 264 210 public static function dashboard_page() 265 211 { 266 include(plugin_dir_path(__FILE__) . '../../templates/ml-widgets.php');267 212 include(plugin_dir_path(__FILE__) . '../../templates/ml-dashboard.php'); 268 213 } 269 214 270 public static function widget_page() 271 { 272 include(plugin_dir_path(__FILE__) . '../../templates/ml-widgets.php'); 273 } 274 275 public static function semantic_search_queue_page() 276 { 277 include plugin_dir_path(__FILE__) . '../../templates/semantic-search-queue.php'; 278 } 279 280 public static function semantic_search_add_page() 281 { 282 include plugin_dir_path(__FILE__) . '../../templates/semantic-search-add.php'; 283 } 284 285 public static function semantic_search_edit_page() 286 { 287 include plugin_dir_path(__FILE__) . '../../templates/semantic-search-edit.php'; 288 } 289 290 public static function dynamic_pricing_queue_page() 291 { 292 include plugin_dir_path(__FILE__) . '../../templates/dynamic-pricing-queue.php'; 293 } 294 295 public static function dynamic_pricing_queue_view_page() 296 { 297 include plugin_dir_path(__FILE__) . '../../templates/dynamic-pricing-view.php'; 298 } 299 300 public static function dynamic_pricing_train_page() 301 { 302 include plugin_dir_path(__FILE__) . '../../templates/dynamic-pricing-train.php'; 303 } 304 305 public static function dynamic_pricing_predict_page() 306 { 307 include plugin_dir_path(__FILE__) . '../../templates/dynamic-pricing-predict.php'; 215 /** 216 * Render chatbot widget in admin footer. 217 * 218 * @since 5.5.0 219 */ 220 public static function render_chatbot_widget() 221 { 222 require plugin_dir_path(__FILE__) . '../../templates/n8n-chatbot-widget.php'; 223 } 224 225 public static function settings_page() 226 { 227 // Redux Framework handles this page 228 } 229 230 public static function feeds_page() 231 { 232 require_once plugin_dir_path(__FILE__) . '../../includes/feeds/class-product-feed-generator.php'; 233 require_once plugin_dir_path(__FILE__) . '../../includes/feeds/class-page-feed-generator.php'; 234 require_once plugin_dir_path(__FILE__) . '../../includes/feeds/class-sales-feed-generator.php'; 235 require_once plugin_dir_path(__FILE__) . '../../includes/feeds/class-insights-feed-generator.php'; 236 237 $message = ''; 238 $messageType = ''; 239 240 if (isset($_POST['generate_products']) && check_admin_referer('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce')) { 241 $generator = new \VisualwebsML_ProductFeedGenerator(); 242 $url = $generator->generate(); 243 if ($url) { 244 $message = sprintf(__('Product feed generated successfully: %s', 'visualwebs-ml'), $url); 245 $messageType = 'success'; 246 } else { 247 $message = __('Failed to generate product feed', 'visualwebs-ml'); 248 $messageType = 'error'; 249 } 250 } 251 252 if (isset($_POST['generate_pages']) && check_admin_referer('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce')) { 253 $generator = new \VisualwebsML_PageFeedGenerator(); 254 $url = $generator->generate(); 255 if ($url) { 256 $message = sprintf(__('Page feed generated successfully: %s', 'visualwebs-ml'), $url); 257 $messageType = 'success'; 258 } else { 259 $message = __('Failed to generate page feed', 'visualwebs-ml'); 260 $messageType = 'error'; 261 } 262 } 263 264 if (isset($_POST['generate_sales']) && check_admin_referer('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce')) { 265 $generator = new \VisualwebsML_SalesFeedGenerator(); 266 $url = $generator->generate(); 267 if ($url) { 268 $message = sprintf(__('Sales feed generated successfully: %s', 'visualwebs-ml'), $url); 269 $messageType = 'success'; 270 } else { 271 $message = __('Failed to generate sales feed', 'visualwebs-ml'); 272 $messageType = 'error'; 273 } 274 } 275 276 if (isset($_POST['generate_insights']) && check_admin_referer('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce')) { 277 $generator = new \VisualwebsML_InsightsFeedGenerator(); 278 $url = $generator->generate(); 279 if ($url) { 280 $message = sprintf(__('Insights feed generated successfully: %s', 'visualwebs-ml'), $url); 281 $messageType = 'success'; 282 } else { 283 $message = __('Failed to generate insights feed', 'visualwebs-ml'); 284 $messageType = 'error'; 285 } 286 } 287 288 ?> 289 <div class="wrap"> 290 <h1><?php esc_html_e('Feed Generation', 'visualwebs-ml'); ?></h1> 291 292 <?php if ($message): ?> 293 <div class="notice notice-<?php echo esc_attr($messageType); ?> is-dismissible"> 294 <p><?php echo esc_html($message); ?></p> 295 </div> 296 <?php endif; ?> 297 298 <p><?php esc_html_e('Manually trigger feed generation. Feeds are also generated automatically via cron jobs.', 'visualwebs-ml'); ?></p> 299 300 <div class="card"> 301 <h2><?php esc_html_e('Product Feed', 'visualwebs-ml'); ?></h2> 302 <p><?php esc_html_e('Generates JSON feed of all products for semantic search synchronization. Auto-generated hourly.', 'visualwebs-ml'); ?></p> 303 <form method="post"> 304 <?php wp_nonce_field('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce'); ?> 305 <button type="submit" name="generate_products" class="button button-primary"> 306 <?php esc_html_e('Generate Product Feed', 'visualwebs-ml'); ?> 307 </button> 308 </form> 309 </div> 310 311 <div class="card"> 312 <h2><?php esc_html_e('Page Feed', 'visualwebs-ml'); ?></h2> 313 <p><?php esc_html_e('Generates JSON feed of all pages and posts for content search. Auto-generated daily at 2 AM.', 'visualwebs-ml'); ?></p> 314 <form method="post"> 315 <?php wp_nonce_field('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce'); ?> 316 <button type="submit" name="generate_pages" class="button button-primary"> 317 <?php esc_html_e('Generate Page Feed', 'visualwebs-ml'); ?> 318 </button> 319 </form> 320 </div> 321 322 <div class="card"> 323 <h2><?php esc_html_e('Sales Feed', 'visualwebs-ml'); ?></h2> 324 <p><?php esc_html_e('Generates JSON feed of sales data for SmartPricing AI training. Auto-generated daily at 2 AM.', 'visualwebs-ml'); ?></p> 325 <form method="post"> 326 <?php wp_nonce_field('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce'); ?> 327 <button type="submit" name="generate_sales" class="button button-primary"> 328 <?php esc_html_e('Generate Sales Feed', 'visualwebs-ml'); ?> 329 </button> 330 </form> 331 </div> 332 333 <div class="card"> 334 <h2><?php esc_html_e('Insights Feed', 'visualwebs-ml'); ?></h2> 335 <p><?php esc_html_e('Generates JSON feed of dashboard insights (sales, bestsellers, reviews, registrations) for SaaS widgets. Auto-generated daily at 3 AM.', 'visualwebs-ml'); ?></p> 336 <form method="post"> 337 <?php wp_nonce_field('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce'); ?> 338 <button type="submit" name="generate_insights" class="button button-primary"> 339 <?php esc_html_e('Generate Insights Feed', 'visualwebs-ml'); ?> 340 </button> 341 </form> 342 </div> 343 344 <div class="card"> 345 <h2><?php esc_html_e('Feed Files', 'visualwebs-ml'); ?></h2> 346 <p><?php esc_html_e('Generated feeds are stored in:', 'visualwebs-ml'); ?> <code><?php echo esc_html(wp_upload_dir()['baseurl'] . '/visualwebs-ml/'); ?></code></p> 347 <ul> 348 <li><strong>product_feed.json</strong> - Product catalog</li> 349 <li><strong>page_feed.json</strong> - Pages and posts</li> 350 <li><strong>sales_feed.json</strong> - Sales data for SmartPricing</li> 351 <li><strong>insights_feed.json</strong> - Dashboard insights and statistics</li> 352 </ul> 353 </div> 354 </div> 355 <?php 308 356 } 309 357 -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Block/Adminhtml/Dashboard.php
r3337157 r3476789 320 320 * Retrieve the existing widgets for the dashboard. 321 321 * 322 * @return array An array of user widgets .322 * @return array An array of user widgets (empty - config removed). 323 323 */ 324 324 public function getUserWidgets() 325 325 { 326 return $this->helper->getUserWidgets(); 326 // User widgets config removed (now managed in SaaS) 327 return []; 327 328 } 328 329 -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Block/ChatWidget.php
r3337157 r3476789 40 40 41 41 /** 42 * Retrieve the URL for the admin chatbot proxy API. 43 * 44 * @return string 45 */ 46 public function getAdminChatApiUrl() 47 { 48 return $this->helper->getAdminChatApiUrl(); 49 } 50 51 /** 42 52 * Check if the chatbot is enabled. 43 53 * … … 47 57 { 48 58 return $this->helper->getIsChatbotEnabled(); 59 } 60 61 /** 62 * Check if the admin chatbot is enabled. 63 * 64 * @return bool 65 */ 66 public function getIsAdminChatbotEnabled() 67 { 68 return $this->helper->getIsAdminChatbotEnabled(); 49 69 } 50 70 … … 235 255 return $this->helper->getCurrentFrontendLanguageShort(); 236 256 } 257 258 /** 259 * Get chatbot namespace. 260 * 261 * @return string 262 */ 263 public function getChatbotNamespace() 264 { 265 return $this->helper->getChatbotNamespace(); 266 } 267 268 /** 269 * Get chatbot store ID. 270 * 271 * @return string 272 */ 273 public function getChatbotStoreId() 274 { 275 return $this->helper->getChatbotStoreId(); 276 } 277 278 /** 279 * Get "Powered by" text. 280 * 281 * @return string 282 */ 283 public function getChatbotPoweredByText() 284 { 285 return $this->helper->getChatbotPoweredByText(); 286 } 287 288 /** 289 * Get "Powered by" link. 290 * 291 * @return string 292 */ 293 public function getChatbotPoweredByLink() 294 { 295 return $this->helper->getChatbotPoweredByLink(); 296 } 297 298 /** 299 * Get chatbot assets base URL. 300 * 301 * @return string 302 */ 303 public function getChatbotAssetsBaseUrl() 304 { 305 return $this->helper->getChatbotAssetsBaseUrl(); 306 } 307 308 /** 309 * Get conversation unique session ID. 310 * 311 * @return string 312 */ 313 public function getConversationUniqueSessionId() 314 { 315 return $this->helper->getConversationUniqueSessionId(); 316 } 237 317 } -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Helper/Chatbot.php
r3319473 r3476789 10 10 class Chatbot extends \Visualwebs\ML\Helper\Data 11 11 { 12 private const SAAS_API_BASE_URL = 'https://saas.visualwebs.eu/api/v1'; 13 private const SAAS_CHATBOT_CONFIG_PATH = '/chatbot/public/config/'; 14 private const SAAS_CHATBOT_CACHE_TTL = DAY_IN_SECONDS; 15 private const SAAS_CHATBOT_ASSETS_BASE_URL = 'https://saas.visualwebs.eu/assets/chatbot/v1'; 16 12 17 public const CHATBOT_DEFAULT_HEADER_TITLE = 'Support'; 13 18 public const CHATBOT_DEFAULT_OFFLINE_MESSAGE = 'Our support system is OFF now, please try later.'; … … 23 28 $isChatbotEnabled = $this->getConfig('chatbot_enabled', false); 24 29 $isVisualwebsMlServicesEnabled = $this->getIsVisualwebsMlServicesEnabled(); 25 return $isChatbotEnabled && $isVisualwebsMlServicesEnabled; 30 $chatApiUrl = $this->getChatApiUrl(); 31 32 return $isChatbotEnabled && $isVisualwebsMlServicesEnabled && !empty($chatApiUrl); 33 } 34 35 /** 36 * Check if the admin chatbot is enabled. 37 * 38 * @return bool 39 */ 40 public function getIsAdminChatbotEnabled() 41 { 42 $isAdminEnabled = $this->getConfig('admin_enabled', false); 43 $isVisualwebsMlServicesEnabled = $this->getIsVisualwebsMlServicesEnabled(); 44 $chatApiUrl = $this->getChatApiUrl(); 45 46 return $isAdminEnabled && $isVisualwebsMlServicesEnabled && !empty($chatApiUrl); 26 47 } 27 48 … … 117 138 public function getChatbotMainBackgroundColor() 118 139 { 119 $ backgroundColor = $this->getConfig('chatbot_main_background_color', true);120 return $ backgroundColor ?: "#000";140 $saasColor = $this->getSaasWidgetConfigValue('color'); 141 return $saasColor ? (string)$saasColor : "#000"; 121 142 } 122 143 … … 128 149 public function getChatbotDisclaimerUrl() 129 150 { 130 $ disclaimerUrl = $this->getConfig('chatbot_disclaimer_url', '');131 return $ disclaimerUrl ? get_page_link(get_page_by_path($disclaimerUrl)) : get_page_link(get_page_by_path('about-us'));151 $saasDisclaimer = $this->getSaasWidgetConfigValue('disclaimer_url'); 152 return $saasDisclaimer ? (string)$saasDisclaimer : ''; 132 153 } 133 154 … … 139 160 public function getChatbotContext() 140 161 { 141 $chatBotContext = $this->getChatbotContextValue(); 142 return $chatBotContext ? trim($chatBotContext) : __('Act as E-commerce expert', 'visualwebs-ml'); 143 } 144 145 /** 146 * Retrieve the context value for the chatbot in config. 147 * 148 * @return mixed The user defined context value for the chatbot. 149 */ 150 public function getChatbotContextValue() 151 { 152 $chatBotContext = $this->getConfig('chatbot_context', ''); 153 return $chatBotContext; 162 $saasPrompt = $this->getSaasWidgetConfigValue('prompt'); 163 return $saasPrompt ? trim((string)$saasPrompt) : __('Act as E-commerce expert', 'visualwebs-ml'); 154 164 } 155 165 … … 161 171 public function getChatbotHeaderTitle() 162 172 { 163 $ chatBotHeadTitle = $this->getConfig('chatbot_header_title', '');173 $saasTitle = $this->getSaasWidgetConfigValue('title'); 164 174 $defaultChatBotHeadTitle = self::CHATBOT_DEFAULT_HEADER_TITLE; 165 return $ chatBotHeadTitle ? trim($chatBotHeadTitle) : $defaultChatBotHeadTitle;175 return $saasTitle ? trim((string)$saasTitle) : __($defaultChatBotHeadTitle, 'visualwebs-ml'); 166 176 } 167 177 … … 173 183 public function getChatbotOfflineMessage() 174 184 { 175 $ chatBotOfflineMessage = $this->getConfig('chatbot_offline_message', '');185 $saasOfflineMessage = $this->getSaasWidgetConfigValue('offline_message'); 176 186 $defaultChatBotOfflineMessage = self::CHATBOT_DEFAULT_OFFLINE_MESSAGE; 177 return $ chatBotOfflineMessage ? trim($chatBotOfflineMessage) : $defaultChatBotOfflineMessage;187 return $saasOfflineMessage ? trim((string)$saasOfflineMessage) : __($defaultChatBotOfflineMessage, 'visualwebs-ml'); 178 188 } 179 189 … … 185 195 public function getChatbotLogoUrl() 186 196 { 187 if ($imagePath = $this->getConfig('chatbot_logo')) { 188 if (isset($imagePath['url'])) { 189 return $imagePath['url']; 190 } 191 } 192 193 return ''; 197 $saasLogo = $this->getSaasWidgetConfigValue('logo'); 198 return $saasLogo ? (string)$saasLogo : ''; 199 } 200 201 /** 202 * Retrieve SaaS chatbot webhook URL. 203 * 204 * @return string 205 */ 206 public function getChatApiUrl() 207 { 208 $webhookUrl = $this->getSaasChatbotConfigValue('n8n_webhook_url'); 209 return $webhookUrl ? esc_url_raw((string) $webhookUrl) : ''; 210 } 211 212 /** 213 * Retrieve admin chatbot proxy endpoint (secured local REST endpoint). 214 * 215 * @return string 216 */ 217 public function getAdminChatApiUrl() 218 { 219 $nonce = wp_create_nonce('visualwebs_ml_admin_chat'); 220 return esc_url_raw(rest_url('visualwebs-ml/v1/chat-api/admin-call?_vwml_nonce=' . rawurlencode($nonce))); 194 221 } 195 222 … … 225 252 } 226 253 254 /** 255 * Returns raw SaaS chatbot config payload (cached). 256 * 257 * @return array|null 258 */ 259 private function getSaasChatbotConfig() 260 { 261 $storeId = $this->getStoreId(); 262 if (empty($storeId)) { 263 return null; 264 } 265 266 $cacheKey = 'vwml_chatbot_config_' . md5($storeId); 267 $cached = get_transient($cacheKey); 268 if (is_array($cached)) { 269 return $cached; 270 } 271 272 $url = self::SAAS_API_BASE_URL . self::SAAS_CHATBOT_CONFIG_PATH . rawurlencode($storeId); 273 $headers = array('Accept' => 'application/json'); 274 $apiKey = $this->getApiKey(); 275 if (!empty($apiKey)) { 276 $headers['x-api-key'] = $apiKey; 277 } 278 279 $response = wp_remote_get( 280 $url, 281 array( 282 'headers' => $headers, 283 'timeout' => 8, 284 ) 285 ); 286 287 if (is_wp_error($response)) { 288 $this->debug('[Visualwebs ML] Failed to fetch SaaS chatbot config: ' . $response->get_error_message()); 289 return null; 290 } 291 292 $status = (int) wp_remote_retrieve_response_code($response); 293 if ($status !== 200) { 294 $this->debug('[Visualwebs ML] Failed to fetch SaaS chatbot config. HTTP status: ' . $status); 295 return null; 296 } 297 298 $body = wp_remote_retrieve_body($response); 299 $decoded = json_decode($body, true); 300 if (!is_array($decoded)) { 301 return null; 302 } 303 304 set_transient($cacheKey, $decoded, self::SAAS_CHATBOT_CACHE_TTL); 305 306 return $decoded; 307 } 308 309 /** 310 * Returns a top-level key from SaaS chatbot config payload. 311 * 312 * @param string $key 313 * @return mixed|null 314 */ 315 private function getSaasChatbotConfigValue($key) 316 { 317 $response = $this->getSaasChatbotConfig(); 318 if (!$response || !isset($response['config']) || !is_array($response['config'])) { 319 return null; 320 } 321 322 return array_key_exists($key, $response['config']) ? $response['config'][$key] : null; 323 } 324 325 /** 326 * Returns a widget_config key from SaaS config. 327 * 328 * @param string $key 329 * @return mixed|null 330 */ 331 private function getSaasWidgetConfigValue($key) 332 { 333 $response = $this->getSaasChatbotConfig(); 334 if (!$response || !isset($response['config']) || !is_array($response['config'])) { 335 return null; 336 } 337 338 $config = $response['config']; 339 if (!isset($config['widget_config'])) { 340 return null; 341 } 342 343 $widgetConfig = $config['widget_config']; 344 if (is_string($widgetConfig)) { 345 $decoded = json_decode($widgetConfig, true); 346 $widgetConfig = is_array($decoded) ? $decoded : array(); 347 } 348 349 return isset($widgetConfig[$key]) ? $widgetConfig[$key] : null; 350 } 351 352 /** 353 * Get chatbot namespace (for multi-bot identification). 354 * 355 * @return string 356 */ 357 public function getChatbotNamespace() 358 { 359 return $this->getSaasChatbotConfigValue('namespace') ?: 'default'; 360 } 361 362 /** 363 * Get chatbot store ID. 364 * 365 * @return string 366 */ 367 public function getChatbotStoreId() 368 { 369 return $this->getStoreId(); 370 } 371 372 /** 373 * Get "Powered by" text for chatbot footer. 374 * 375 * @return string 376 */ 377 public function getChatbotPoweredByText() 378 { 379 return $this->getSaasChatbotConfigValue('powered_by_text') ?: 'Powered by Visualwebs AI'; 380 } 381 382 /** 383 * Get "Powered by" link for chatbot footer. 384 * 385 * @return string 386 */ 387 public function getChatbotPoweredByLink() 388 { 389 return $this->getSaasChatbotConfigValue('powered_by_link') ?: 'https://visualwebs.eu'; 390 } 391 392 /** 393 * Get chatbot assets base URL (where n8n-chat-widget.js is hosted). 394 * 395 * @return string 396 */ 397 public function getChatbotAssetsBaseUrl() 398 { 399 return self::SAAS_CHATBOT_ASSETS_BASE_URL; 400 } 401 227 402 } -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Helper/ChatbotApiCall.php
r3322994 r3476789 10 10 use Visualwebs\ML\Helper\Visualwebs as ApiHelper; 11 11 use Visualwebs\ML\Helper\Chatbot as ChatbotHelper; 12 use Visualwebs\ML\Helper\OpenAi as OpenAiHelper; 13 use Visualwebs\ML\Helper\Pinecone as PineconeHelper; 12 use Visualwebs\ML\Helper\Data as DataHelper; 14 13 15 14 class ChatbotApiCall … … 26 25 27 26 /** 28 * @var \Visualwebs\ML\Helper\OpenAiHelper 29 */ 30 protected $openAiHelper; 31 32 /** 33 * @var \Visualwebs\ML\Helper\PineconeHelper 34 */ 35 protected $pineconeHelper; 27 * @var \Visualwebs\ML\Helper\DataHelper 28 */ 29 protected $dataHelper; 36 30 37 31 /** … … 49 43 $this->apiHelper = new ApiHelper(); 50 44 $this->chatbotHelper = new ChatbotHelper(); 51 $this->openAiHelper = new OpenAiHelper(); 52 $this->pineconeHelper = new PineconeHelper(); 45 $this->dataHelper = new DataHelper(); 53 46 $this->debugEnabled = $this->chatbotHelper->getIsDebugChatbotEnabled(); 54 47 } … … 62 55 * @return void 63 56 */ 64 public function execute() 65 { 66 $frontendBaseUrl = $this->chatbotHelper->getFrontendBaseUrl(); 67 $context = $this->chatbotHelper->getChatbotContextValue(); 68 $input = json_decode(file_get_contents('php://input'), true); 57 public function execute($input = null) 58 { 59 $input = is_array($input) ? $input : json_decode(file_get_contents('php://input'), true); 60 if (!is_array($input)) { 61 $input = array(); 62 } 69 63 70 64 $message = isset($input['message']) ? sanitize_text_field(wp_unslash($input['message'])) : ''; … … 74 68 : []; 75 69 76 // Important, we retrieve here because in frontend is cached 77 $uniqueSessionId = $this->chatbotHelper->getConversationUniqueSessionId(); 78 $openAiApiKey = $this->openAiHelper->getApiKey(); 79 $offMessage = $this->chatbotHelper->getChatbotOfflineMessage(); 80 $useCustomerApiKeysEnabled = $this->chatbotHelper->getUseCustomerApiKeysEnabled(); 81 $domainNamespace = $this->chatbotHelper->getDomainNamespace(); 82 83 if ($useCustomerApiKeysEnabled) { 84 $args = [ 85 'openai_api_key' => $openAiApiKey, 86 'session_id' => $uniqueSessionId, 87 'is_single_prompt' => !$isChat 88 ]; 89 } else { 90 $args = [ 91 'session_id' => $uniqueSessionId, 92 'is_single_prompt' => !$isChat 93 ]; 94 } 95 96 if ($isChat) { 97 98 $args['instructions'] = $context; 99 $args['catalog_search_url'] = $frontendBaseUrl . "?s="; 100 $args['tools'] = ["get_order_status", "get_product_stock"]; 101 $args['tools_endpoint'] = $this->chatbotHelper->getCallbackApiUrl(); 102 $args['current_page_info'] = $currentPageInfo; 103 if ($offMessage && trim($offMessage)) { 104 $args['offline_message'] = $offMessage; 105 } 106 107 if ($this->chatbotHelper->getIsSemanticSearchEnabled()) { 108 109 // We offer Pinecone only at this moment 110 111 $pineconeApiKey = $this->pineconeHelper->getPineconeApiKey(); 112 $pineconeHost = $this->pineconeHelper->getPineconeIndexHost(); 113 $pineconeNamespace = $this->pineconeHelper->getPineconeNamespace(); 114 $vectorSearchMinScore = $this->chatbotHelper->getVectorSearchMinScore(); 115 116 if ($useCustomerApiKeysEnabled) { 117 $args = array_merge($args, [ 118 'pinecone_api_key' => $pineconeApiKey, 119 'pinecone_index_host' => $pineconeHost, 120 'pinecone_namespace' => $pineconeNamespace, 121 'vector_search_min_score' => $vectorSearchMinScore 122 ]); 123 } else { 124 $args = array_merge($args, [ 125 'pinecone_namespace' => $domainNamespace, 126 'vector_search_min_score' => $vectorSearchMinScore 127 ]); 128 } 129 } 130 } 131 132 $this->setArgs($args); 133 134 try { 135 $success = true; 136 if ($isChat) { 137 $reply = $this->apiHelper->sendChatRequest($message, $this->getArgs()); 138 } else { 139 $reply = $this->apiHelper->sendPromptRequest($message, $this->getArgs()); 140 } 141 } catch (\Exception $e) { 142 $reply = __('Sorry, a problem happened, please try again.', 'visualwebs-ml'); 143 $success = false; 144 } 145 146 wp_send_json([ 147 'result' => $reply, 148 'success' => $success 149 ]); 70 if (empty($message)) { 71 return array( 72 'result' => '', 73 'success' => false, 74 'error' => __('Message is required.', 'visualwebs-ml'), 75 ); 76 } 77 78 $payload = array( 79 'message' => $message, 80 'user_message' => $message, 81 'platform' => 'wordpress-frontend', 82 'store_id' => $this->dataHelper->getStoreId(), 83 'params' => array( 84 'session_id' => $this->chatbotHelper->getConversationUniqueSessionId(), 85 'is_single_prompt' => !$isChat, 86 'current_page_info' => $currentPageInfo, 87 'instructions' => $this->chatbotHelper->getChatbotContextValue(), 88 'offline_message' => $this->chatbotHelper->getChatbotOfflineMessage(), 89 ), 90 ); 91 92 error_log('[Visualwebs ML][Chatbot][request] ' . wp_json_encode(array( 93 'endpoint' => $this->chatbotHelper->getChatApiUrl(), 94 'payload_keys' => array_keys($payload), 95 'scope' => 'frontend', 96 ))); 97 98 $response = $this->apiHelper->sendWebhookRequest( 99 $this->chatbotHelper->getChatApiUrl(), 100 $payload, 101 array('x-api-key' => $this->dataHelper->getApiKey()) 102 ); 103 104 error_log('[Visualwebs ML][Chatbot][response] ' . wp_json_encode(array( 105 'scope' => 'frontend', 106 'success' => empty($response['error']), 107 'has_result' => isset($response['result']) || isset($response['reply']) || isset($response['message']), 108 ))); 109 110 return array( 111 'result' => isset($response['result']) 112 ? $response['result'] 113 : (isset($response['reply']) ? $response['reply'] : (isset($response['message']) ? $response['message'] : '')), 114 'success' => empty($response['error']), 115 'error' => isset($response['error']) ? $response['error'] : '', 116 ); 117 } 118 119 /** 120 * Execute admin chatbot call via secure local proxy endpoint. 121 * 122 * @param array|null $input 123 * @return array 124 */ 125 public function execute_admin($input = null) 126 { 127 $input = is_array($input) ? $input : json_decode(file_get_contents('php://input'), true); 128 if (!is_array($input)) { 129 $input = array(); 130 } 131 132 $message = isset($input['message']) ? sanitize_text_field(wp_unslash($input['message'])) : ''; 133 if (empty($message)) { 134 return array( 135 'result' => '', 136 'success' => false, 137 'error' => __('Message is required.', 'visualwebs-ml'), 138 ); 139 } 140 141 $payload = array( 142 'message' => $message, 143 'user_message' => $message, 144 'platform' => 'wordpress-admin', 145 'store_id' => $this->dataHelper->getStoreId(), 146 'params' => array( 147 'session_id' => 'admin_' . get_current_user_id() . '_' . wp_generate_uuid4(), 148 'is_single_prompt' => false, 149 'is_admin' => true, 150 'current_page_info' => array( 151 'full_action_name' => DataHelper::getFullActionName(), 152 'params' => array(), 153 ), 154 ), 155 ); 156 157 error_log('[Visualwebs ML][Chatbot][request] ' . wp_json_encode(array( 158 'endpoint' => $this->chatbotHelper->getChatApiUrl(), 159 'payload_keys' => array_keys($payload), 160 'scope' => 'admin', 161 'user_id' => get_current_user_id(), 162 ))); 163 164 $response = $this->apiHelper->sendWebhookRequest( 165 $this->chatbotHelper->getChatApiUrl(), 166 $payload, 167 array('x-api-key' => $this->dataHelper->getApiKey()) 168 ); 169 170 error_log('[Visualwebs ML][Chatbot][response] ' . wp_json_encode(array( 171 'scope' => 'admin', 172 'success' => empty($response['error']), 173 'has_result' => isset($response['result']) || isset($response['reply']) || isset($response['message']), 174 ))); 175 176 return array( 177 'result' => isset($response['result']) 178 ? $response['result'] 179 : (isset($response['reply']) ? $response['reply'] : (isset($response['message']) ? $response['message'] : '')), 180 'success' => empty($response['error']), 181 'error' => isset($response['error']) ? $response['error'] : '', 182 ); 150 183 } 151 184 -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Helper/Data.php
r3339941 r3476789 33 33 34 34 /** 35 * Retrieve Visualwebs API key. 36 * 37 * @return string 38 */ 39 public function getApiKey() 40 { 41 return (string) $this->getConfig('api_key', ''); 42 } 43 44 /** 45 * Retrieve SaaS store UUID. 46 * 47 * @return string 48 */ 49 public function getStoreId() 50 { 51 return (string) $this->getConfig('store_id', ''); 52 } 53 54 /** 35 55 * Check if the Visualwebs ML module is enabled. 36 56 * … … 78 98 return $isMlServicesEnabled && $isMainModuleEnabled && $mlServicesApiKey; 79 99 } 80 81 /**82 * Check if the use of customer API keys is enabled.83 *84 * This method retrieves the configuration setting that determines whether85 * the use of customer OpenAI/PInecone API keys is enabled for the Visualwebs ML services.86 *87 * @return bool Returns true if the use of customer API keys is enabled, false otherwise.88 */89 public function getUseCustomerApiKeysEnabled()90 {91 $isUseCustomerApiKeysEnabled = $this->getConfig('use_customer_api_keys');92 93 return $isUseCustomerApiKeysEnabled;94 }95 96 100 /** 97 101 * Check if Dynamic Pricing is enabled. … … 123 127 124 128 /** 125 * Get the minimum profit percentage for Dynamic Pricing.126 *127 * @return float128 */129 public function getDynamicPricingMinProfit()130 {131 return (float) $this->getConfig('dynamic_pricing_min_profit', 10);132 }133 134 /**135 * Get the maximum profit percentage for Dynamic Pricing.136 *137 * @return float138 */139 public function getDynamicPricingMaxProfit()140 {141 return (float) $this->getConfig('dynamic_pricing_max_profit', 35);142 }143 144 /**145 129 * Check if dynamic pricing can replace prices (enabled AND replace prices). 146 130 * … … 150 134 { 151 135 return $this->getIsDynamicPricingEnabled() && $this->getIsDynamicPricingReplacePrices(); 152 }153 154 /**155 * Check if storing dynamic data (like product price and stock) is enabled.156 *157 * @return bool158 */159 public function getIsStoreDynamicDataEnabled()160 {161 return (bool) $this->getConfig('vector_store_dynamic_data', false);162 136 } 163 137 … … 197 171 $currencySymbol = get_woocommerce_currency_symbol(); 198 172 return $currencySymbol ?: "€"; 199 }200 201 /**202 * Retrieve user widgets configuration.203 *204 * This method fetches the user widgets configuration value from the store's scope configuration.205 *206 * @return string JSON encoded string of user widgets configuration.207 * Returns an empty JSON array "[]" if no configuration is found.208 */209 210 public function getUserWidgets()211 {212 $userWidgets = $this->getConfig('user_widgets');213 $processedWidgets = [];214 215 if (!empty($userWidgets['redux_repeater_data'])) {216 foreach ($userWidgets['redux_repeater_data'] as $index => $widgetData) {217 $processedWidgets[] = [218 'widget_title' => $userWidgets['widget_title'][$index] ?? '',219 'widget_instructions' => $userWidgets['widget_instructions'][$index] ?? '',220 'widget_render_el' => $userWidgets['widget_render_el'][$index] ?? '',221 'widget_source_el' => $userWidgets['widget_source_el'][$index] ?? '',222 ];223 }224 }225 226 return $processedWidgets;227 173 } 228 174 … … 322 268 { 323 269 $content = ''; 324 $isStoreDynamicDataEnabled = $this->getIsStoreDynamicDataEnabled(); 270 // Store dynamic data config was removed (now managed in SaaS feed generation) 271 $isStoreDynamicDataEnabled = false; 325 272 326 273 if (post_type_exists($postType)) { -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Helper/Enqueue.php
r3337202 r3476789 3 3 namespace Visualwebs\ML\Helper; 4 4 5 use Visualwebs\ML\Block\ChatWidget;6 use Visualwebs\ML\Block\Adminhtml\Widget;7 use Visualwebs\ML\Block\Adminhtml\Dashboard;8 use Visualwebs\ML\Helper\Data as Helper;9 use Visualwebs\ML\Model\Config\Source\MLModelType;10 11 5 class Enqueue 12 6 { 13 private $widget;14 private $dashboard;15 private $chatWidget;16 17 7 /** 18 * Constructor to initialize dependencies.8 * Constructor. 19 9 */ 20 10 public function __construct() 21 11 { 22 $this->widget = new Widget(); 23 $this->dashboard = new Dashboard(); 24 $this->chatWidget = new ChatWidget(); 12 // Lightweight enqueue class for 5.5.0+ 25 13 } 26 14 … … 32 20 public function enqueue_admin_scripts($hook) 33 21 { 34 wp_enqueue_style('visualwebs-ml-admin-style', plugin_dir_url(__FILE__) . '../../../assets/css/admin-style.css', [], false); 35 36 if ($this->widget->getIsWidgetEnabled()) { 37 38 $nonce = $this->widget->getAppNonce(); 39 $cache_key = Helper::CACHE_TRANSIENT; 40 $cached_data = get_transient($cache_key); 41 42 if ($cached_data === false) { 43 $cached_data = [ 44 'salesLiveDataset' => $this->widget->getStoreSalesData(), 45 'bestsellerLiveDataset' => $this->widget->getProductBestSellersData(), 46 'bestsellerMeta' => $this->widget->getProductBestSellersMetadata(), 47 'reviewsLiveDataset' => $this->widget->getProductsReviewsData(), 48 'customerRegistrationLiveDataset' => $this->widget->getCustomerRegistrationData(), 49 'MLApiUrl' => $this->widget->getMLApiUrl(), 50 'chatApiUrl' => $this->widget->getChatApiUrl(), 51 'timeseriesModel' => MLModelType::TYPE_TEMPORAL_SERIES_DEFAULT, 52 'sentimentModel' => MLModelType::TYPE_TEXT_SENTIMENT, 53 'spamModel' => MLModelType::TYPE_SPAM_CLASSIFIER, 54 'userWidgets' => $this->widget->getUserWidgets() ?: [] 55 ]; 56 57 set_transient($cache_key, $cached_data, HOUR_IN_SECONDS); 58 } 59 60 $dynamic_data = [ 61 'productId' => $this->widget->getProduct() ? $this->widget->getProduct()->get_id() : '0', 62 'productRef' => $this->widget->getProduct() ? $this->widget->getProduct()->get_sku() : 'NA', 63 'productLiveDataset' => $this->widget->getProduct() ? $this->widget->getProductData() : [], 64 'userLanguage' => $this->widget->getCurrentAdminUserLanguageShort(), 65 'nonce' => $nonce 66 ]; 67 68 $translated_titles = [ 69 'salesPredictorTitle' => esc_html__('Sales Predictor', 'visualwebs-ml'), 70 'reviewsSentimentTitle' => esc_html__('Reviews Sentiment', 'visualwebs-ml'), 71 'bestsellersPredictorTitle' => esc_html__('Bestsellers Predictor', 'visualwebs-ml'), 72 'reviewsSpamTitle' => esc_html__('Reviews Spam', 'visualwebs-ml'), 73 'apiUsageTitle' => esc_html__('API usage', 'visualwebs-ml'), 74 'chatbotHistoryTitle' => esc_html__('Chatbot history', 'visualwebs-ml'), 75 'customerRegisterPredictorTitle' => esc_html__('Customer Register Predictor', 'visualwebs-ml'), 76 'productPredictorTitle' => esc_html__('Product Predictor', 'visualwebs-ml'), 77 'promptTitle' => esc_html__('Prompt', 'visualwebs-ml'), 78 'seoGeneratorTitle' => esc_html__('SEO generator', 'visualwebs-ml'), 79 'seoGeneratorDescriptionTitle' => esc_html__('SEO generator - Description', 'visualwebs-ml'), 80 'seoGeneratorShortDescriptionTitle' => esc_html__('SEO generator - Short description', 'visualwebs-ml') 81 ]; 82 83 $final_data = array_merge($cached_data, $dynamic_data, $translated_titles); 84 85 wp_enqueue_script( 86 'visualwebs-ml-app-remote-js', 87 'https://ml.visualwebs.eu/app.js', 88 array(), 89 false, 90 ['in_footer' => true, 'strategy' => 'defer'] 91 ); 92 93 wp_enqueue_script( 94 'visualwebs-widget-js', 95 plugin_dir_url(__FILE__) . '../../../assets/js/widget.js', 96 array(), 97 false, 98 ['in_footer' => true, 'strategy' => 'defer'] 99 ); 100 101 wp_localize_script('visualwebs-widget-js', 'visualwebsAiWidgetData', $final_data); 102 103 } 104 105 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required for this specific use case. 106 if (isset($_GET['page']) && $_GET['page'] === 'visualwebs-ml-dashboard') { 107 108 if ($this->dashboard->getIsVisualwebsMlServicesEnabled()) { 109 110 $stats = $this->dashboard->getStats(); 111 $userLanguage = $this->dashboard->getCurrentAdminUserLanguageShort(); 112 $currency_symbol = html_entity_decode($this->dashboard->getStoreCurrencySign()); 113 $nonce = $this->dashboard->getAppNonce(); 114 $clear_cache_url = esc_url(admin_url('admin.php?page=visualwebs-ml-dashboard&clear_cache=true&_wpnonce=' . wp_create_nonce('clear_cache_action'))); 115 116 wp_enqueue_script( 117 'visualwebs-dashboard-js', 118 plugin_dir_url(__FILE__) . '../../../assets/js/dashboard.js', 119 array(), 120 false, 121 ['in_footer' => true, 'strategy' => 'defer'] 122 ); 123 124 wp_localize_script('visualwebs-dashboard-js', 'visualwebsAiDashboardData', [ 125 'stats' => $stats, 126 'currencySymbol' => $currency_symbol, 127 'clearCacheUrl' => $clear_cache_url, 128 'userLanguage' => $userLanguage, 129 'nonce' => $nonce 130 ]); 131 132 } 133 } 134 135 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required for this specific use case. 136 if (isset($_GET['page']) && $_GET['page'] === 'visualwebs-ml-semantic-add') { 137 wp_enqueue_script( 138 'visualwebs-ml-semantic-search', 139 plugin_dir_url(__FILE__) . '../../../assets/js/semantic-search.js', 140 array('jquery'), 141 false, 142 ['in_footer' => true, 'strategy' => 'defer'] 143 ); 144 145 wp_localize_script( 146 'visualwebs-ml-semantic-search', 147 'visualwebs_ml_search', 148 array( 149 'ajax_url' => admin_url('admin-ajax.php') 150 ) 151 ); 152 153 } 154 22 // Legacy widgets and dashboard removed in 5.5.0 (now managed in SaaS dashboard) 23 // Legacy chatbot removed in 5.5.0 (now uses n8n-chatbot-widget.php template) 24 // Legacy semantic search admin removed in 5.5.0 25 // Legacy admin styles removed in 5.5.0 155 26 } 156 27 … … 160 31 public function enqueue_frontend_scripts() 161 32 { 162 if ($this->chatWidget->getIsChatbotEnabled()) { 163 $chatApiUrl = $this->chatWidget->getChatApiUrl(); 164 $chatbotCurrentPageInfo = $this->chatWidget->getChatbotCurrentPageInfoForWidget(); 165 $linksColor = $this->chatWidget->getChatbotMainBackgroundColor(); 166 $mainBackgroundColor = $this->chatWidget->getChatbotMainBackgroundColor(); 167 $chatbotHeaderTitle = $this->chatWidget->getChatbotHeaderTitle(); 168 $chatbotDisclaimerUrl = $this->chatWidget->getChatbotDisclaimerUrl(); 169 $chatbotOfflineMessage = $this->chatWidget->getChatbotOfflineMessage(); 170 $chatbotLogoUrl = $this->chatWidget->getChatbotLogoUrl(); 171 $userLanguage = $this->chatWidget->getCurrentFrontendLanguageShort(); 172 173 wp_enqueue_script( 174 'visualwebs-ml-app-remote-js', 175 'https://ml.visualwebs.eu/app.js', 176 array(), 177 false, 178 ['in_footer' => true, 'strategy' => 'defer'] 179 ); 180 181 wp_enqueue_script('visualwebs-chatbot-js', plugin_dir_url(__FILE__) . '../../../assets/js/chatbot.js', ['visualwebs-ml-app-remote-js'], false, ['in_footer' => true, 'strategy' => 'defer']); 182 183 $inline_data = 'var visualwebsAiChatbotData = ' . wp_json_encode([ 184 "chatApiUrl" => $chatApiUrl, 185 "uniqueSessionId" => '', 186 "chatbotCurrentPageInfo" => $chatbotCurrentPageInfo, 187 "mainBackgroundColor" => $mainBackgroundColor, 188 "linksColor" => $linksColor, 189 "chatbotHeaderTitle" => $chatbotHeaderTitle, 190 "chatbotDisclaimerUrl" => $chatbotDisclaimerUrl, 191 "chatbotOfflineMessage" => $chatbotOfflineMessage, 192 "chatbotLogoUrl" => $chatbotLogoUrl, 193 "userLanguage" => $userLanguage, 194 'restUrl' => esc_url_raw(rest_url('visualwebs-ml/v1/get-nonce')) 195 ]) . ';'; 196 197 wp_add_inline_script('visualwebs-chatbot-js', $inline_data, 'before'); 198 } 33 // Legacy chatbot enqueue removed in 5.5.0 34 // Now uses n8n-chatbot-widget.php template loaded via wp_footer hook 35 // See: class-visualwebs-ml.php render_frontend_chatbot_widget() 199 36 } 200 37 } -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Helper/Visualwebs.php
r3337157 r3476789 111 111 112 112 /** 113 * Sends payload to an explicit webhook URL. 114 * 115 * @param string $url 116 * @param array $data 117 * @param array $headers 118 * @return array 119 */ 120 public function sendWebhookRequest($url, $data = [], $headers = []) 121 { 122 $visualwebsApi = $this->getVisualwebsInstance(); 123 return $visualwebsApi->sendWebhookRequest($url, $data, $headers); 124 } 125 126 /** 113 127 * Sends a vector upsert request to the Visualwebs API. 114 128 * -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Model/Api/Visualwebs.php
r3320111 r3476789 97 97 98 98 /** 99 * Sends a request to an explicit webhook URL (used by SaaS chatbot n8n endpoint). 100 * 101 * @param string $url 102 * @param array $data 103 * @param array $extraHeaders 104 * @return array 105 */ 106 public function sendWebhookRequest($url, $data = [], $extraHeaders = []) 107 { 108 if (empty($url)) { 109 return [ 110 'error' => true, 111 'message' => 'Missing webhook URL', 112 ]; 113 } 114 115 $headers = [ 116 'Content-Type' => 'application/json', 117 'Accept' => 'application/json', 118 ]; 119 120 // Keep compatibility with existing API key auth model. 121 if (!empty($this->apiKey)) { 122 $headers['Authorization'] = 'Bearer ' . $this->apiKey; 123 $headers['x-api-key'] = $this->apiKey; 124 } 125 126 if (!empty($extraHeaders) && is_array($extraHeaders)) { 127 $headers = array_merge($headers, $extraHeaders); 128 } 129 130 $response = wp_remote_post( 131 $url, 132 [ 133 'headers' => $headers, 134 'timeout' => 20, 135 'body' => wp_json_encode($data), 136 ] 137 ); 138 139 if (is_wp_error($response)) { 140 return [ 141 'error' => true, 142 'message' => $response->get_error_message(), 143 ]; 144 } 145 146 $status = (int) wp_remote_retrieve_response_code($response); 147 $body = wp_remote_retrieve_body($response); 148 $decoded = json_decode($body, true); 149 150 if ($status < 200 || $status >= 300) { 151 return [ 152 'error' => true, 153 'message' => 'Webhook response status: ' . $status, 154 'raw' => is_array($decoded) ? $decoded : $body, 155 ]; 156 } 157 158 return is_array($decoded) ? $decoded : ['result' => (string) $body]; 159 } 160 161 /** 99 162 * Sends a spam prediction request to the specified endpoint. 100 163 * -
visualwebs-ml/tags/5.5.0/vendor/visualwebs-ml/Model/MLModel.php
r3326860 r3476789 658 658 659 659 $tomorrow = gmdate('Y-m-d', strtotime('+1 day')); 660 $min_pct = $this->mainHelper->getDynamicPricingMinProfit(); 661 $max_pct = $this->mainHelper->getDynamicPricingMaxProfit(); 660 // Default profit margins (config removed, now managed in SaaS) 661 $min_pct = 10; 662 $max_pct = 35; 662 663 $result = []; 663 664 -
visualwebs-ml/tags/5.5.0/visualwebs-ml.php
r3337202 r3476789 4 4 Requires Plugins: redux-framework, woocommerce 5 5 Description: Plugin to embed chatGPT, chatbot, and Machine Learning Widgets in WP. 6 Version: 5. 4.36 Version: 5.5.0 7 7 Requires PHP: 7.4 8 8 Author: Visualwebs 9 9 Author URI: https://visualwebs.eu/product/ai-cloud-suite/ 10 Tested up to: 6. 810 Tested up to: 6.9 11 11 License: GPLv2 or later 12 12 License URI: https://visualwebs.eu/terms-and-conditions/ -
visualwebs-ml/trunk/class-visualwebs-ml.php
r3322994 r3476789 94 94 95 95 add_action( 'plugins_loaded', array( 'Visualwebs\ML\Admin', 'init' ) ); 96 add_action( 'wp_footer', array( $this, 'render_frontend_chatbot_widget' ), 999 ); 96 97 add_action( 97 98 'redux/options/visualwebs_ml_options/saved', … … 135 136 register_rest_route( 136 137 'visualwebs-ml/v1', 138 'chat-api/admin-call', 139 array( 140 'methods' => 'POST', 141 'callback' => array( $this, 'handle_admin_chat_api_call' ), 142 'permission_callback' => array( $this, 'check_admin_chat_permissions' ), 143 ) 144 ); 145 146 register_rest_route( 147 'visualwebs-ml/v1', 137 148 'ml-api/call', 138 149 array( … … 156 167 require_once __DIR__ . '/includes/cronjobs.php'; 157 168 require_once __DIR__ . '/includes/hooks/entity-save.php'; 169 require_once __DIR__ . '/includes/hooks/event-dispatcher.php'; 158 170 require_once __DIR__ . '/includes/helpers/content-generator.php'; 171 require_once __DIR__ . '/includes/feeds/class-insights-feed-generator.php'; 159 172 } 160 173 … … 174 187 175 188 /** 189 * Render chatbot widget in frontend footer. 190 * 191 * @since 5.5.0 192 */ 193 public function render_frontend_chatbot_widget() { 194 require plugin_dir_path( __FILE__ ) . 'templates/n8n-chatbot-widget.php'; 195 } 196 197 /** 176 198 * Handle chat API callback 177 199 * … … 189 211 */ 190 212 public function handle_chat_api_call( WP_REST_Request $request ) { 191 $response = $this->chatbot_api_helper->execute( $request->get_query_params() ); 213 $response = $this->chatbot_api_helper->execute( $request->get_json_params() ); 214 return rest_ensure_response( $response ); 215 } 216 217 /** 218 * Validate permissions for admin chatbot endpoint. 219 * 220 * @param WP_REST_Request $request Request object. 221 * @return bool|WP_Error 222 */ 223 public function check_admin_chat_permissions( WP_REST_Request $request ) { 224 if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) { 225 return new WP_Error( 'vwml_forbidden', __( 'You are not allowed to use admin chatbot.', 'visualwebs-ml' ), array( 'status' => 403 ) ); 226 } 227 228 $nonce = sanitize_text_field( (string) $request->get_param( '_vwml_nonce' ) ); 229 if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'visualwebs_ml_admin_chat' ) ) { 230 return new WP_Error( 'vwml_invalid_nonce', __( 'Invalid admin chatbot nonce.', 'visualwebs-ml' ), array( 'status' => 403 ) ); 231 } 232 233 return true; 234 } 235 236 /** 237 * Handle admin chatbot API call routed through secure local endpoint. 238 * 239 * @param WP_REST_Request $request The REST API request object. 240 * @return WP_REST_Response 241 */ 242 public function handle_admin_chat_api_call( WP_REST_Request $request ) { 243 $response = $this->chatbot_api_helper->execute_admin( $request->get_json_params() ); 192 244 return rest_ensure_response( $response ); 193 245 } -
visualwebs-ml/trunk/composer.json
r3319473 r3476789 9 9 } 10 10 }, 11 "require": {12 "smalot/pdfparser": "^0.18",13 "phpoffice/phpword": "^1.1"14 },15 11 "scripts": { 16 12 "post-install-cmd": [ -
visualwebs-ml/trunk/includes/cronjobs.php
r3330758 r3476789 23 23 use Visualwebs\ML\Helper\Visualwebs as ApiHelper; 24 24 use Visualwebs\ML\Helper\Chatbot as ChatbotHelper; 25 use Visualwebs\ML\Helper\Pinecone as PineconeHelper;26 use Visualwebs\ML\Helper\OpenAi as OpenAiHelper;27 25 28 26 register_activation_hook( 29 27 plugin_dir_path( __DIR__ ) . 'visualwebs-ml.php', 30 28 function () { 31 if ( ! wp_next_scheduled( 'visualwebs_ml_semantic_queue_processor' ) ) { 32 wp_schedule_event( time(), 'every_five_minutes', 'visualwebs_ml_semantic_queue_processor' ); 33 } 34 if ( ! wp_next_scheduled( 'visualwebs_ml_daily_dynamic_pricing' ) ) { 35 $next_run = strtotime( 'tomorrow 07:00:00' ); 36 wp_schedule_event( $next_run, 'daily', 'visualwebs_ml_daily_dynamic_pricing' ); 37 } 38 if ( ! wp_next_scheduled( 'visualwebs_ml_check_dynamic_pricing_batch_jobs' ) ) { 39 wp_schedule_event( time(), 'every_five_minutes', 'visualwebs_ml_check_dynamic_pricing_batch_jobs' ); 40 } 41 if ( ! wp_next_scheduled( 'visualwebs_ml_weekly_dynamic_pricing_training' ) ) { 42 $next_run = strtotime( 'tomorrow 00:00:00' ); 43 wp_schedule_event( $next_run, 'weekly', 'visualwebs_ml_weekly_dynamic_pricing_training' ); 29 // Feed generation cron jobs 30 if ( ! wp_next_scheduled( 'visualwebs_ml_product_feed' ) ) { 31 wp_schedule_event( time(), 'hourly', 'visualwebs_ml_product_feed' ); 32 } 33 if ( ! wp_next_scheduled( 'visualwebs_ml_page_feed' ) ) { 34 $next_run = strtotime( 'tomorrow 02:00:00' ); 35 wp_schedule_event( $next_run, 'daily', 'visualwebs_ml_page_feed' ); 36 } 37 if ( ! wp_next_scheduled( 'visualwebs_ml_sales_feed' ) ) { 38 $next_run = strtotime( 'tomorrow 02:00:00' ); 39 wp_schedule_event( $next_run, 'daily', 'visualwebs_ml_sales_feed' ); 40 } 41 if ( ! wp_next_scheduled( 'visualwebs_ml_insights_feed' ) ) { 42 $next_run = strtotime( 'tomorrow 03:00:00' ); 43 wp_schedule_event( $next_run, 'daily', 'visualwebs_ml_insights_feed' ); 44 44 } 45 45 } … … 49 49 plugin_dir_path( __DIR__ ) . 'visualwebs-ml.php', 50 50 function () { 51 wp_clear_scheduled_hook( 'visualwebs_ml_semantic_queue_processor' ); 52 wp_clear_scheduled_hook( 'visualwebs_ml_daily_dynamic_pricing' ); 53 wp_clear_scheduled_hook( 'visualwebs_ml_check_dynamic_pricing_batch_jobs' ); 54 wp_clear_scheduled_hook( 'visualwebs_ml_weekly_dynamic_pricing_training' ); 55 } 56 ); 57 58 add_filter( 59 'cron_schedules', // phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval 60 function ( $schedules ) { 61 $schedules['every_five_minutes'] = array( 62 'interval' => 300, 63 'display' => __( 'Every 5 Minutes', 'visualwebs-ml' ), 64 ); 65 return $schedules; 66 } 67 ); 68 69 add_action( 70 'visualwebs_ml_semantic_queue_processor', 71 function () { 72 global $wpdb; 73 74 $table_name = $wpdb->prefix . 'visualwebs_ml_semantic_queue'; 75 $batch_limit = 5; 76 77 // Clear completed and disabled jobs. 78 // phpcs:ignore WordPress.DB.DirectDatabaseQuery 79 $wpdb->query( 80 $wpdb->prepare( 81 'DELETE FROM %i 82 WHERE job_status = %d AND job_sync_status = %d', 83 $table_name, 84 0, 85 1 86 ) 87 ); 88 89 // Fetch jobs that are not completed. 90 // phpcs:ignore WordPress.DB.DirectDatabaseQuery 91 $jobs = $wpdb->get_results( 92 $wpdb->prepare( 93 'SELECT * FROM %i 94 WHERE job_sync_status != %d 95 LIMIT %d', 96 $table_name, 97 1, 98 $batch_limit 99 ) 100 ); 101 102 foreach ( $jobs as $job ) { 103 // Process each job. 104 visualwebs_ml_process_queue_item( $job ); 105 106 // Mark the job as completed. 107 // phpcs:ignore WordPress.DB.DirectDatabaseQuery 108 $wpdb->update( 109 $table_name, 110 array( 'job_sync_status' => 1 ), // Mark as completed. 111 array( 'job_id' => $job->job_id ) 112 ); 113 } 114 } 115 ); 116 117 add_action( 118 'visualwebs_ml_daily_dynamic_pricing', 119 'visualwebs_ml_dynamic_pricing' 51 wp_clear_scheduled_hook( 'visualwebs_ml_product_feed' ); 52 wp_clear_scheduled_hook( 'visualwebs_ml_page_feed' ); 53 wp_clear_scheduled_hook( 'visualwebs_ml_sales_feed' ); 54 wp_clear_scheduled_hook( 'visualwebs_ml_insights_feed' ); 55 } 120 56 ); 121 57 … … 250 186 251 187 add_action( 252 'visualwebs_ml_weekly_dynamic_pricing_training',253 function () {254 do_action( 'visualwebs_ml_train_dynamic_pricing' );255 }256 );257 258 add_action(259 188 'visualwebs_ml_train_dynamic_pricing', 260 189 'visualwebs_ml_train_dynamic_pricing' … … 382 311 383 312 384 add_action( 385 'visualwebs_ml_check_dynamic_pricing_batch_jobs', 386 function () { 387 global $wpdb; 388 $helper = new \Visualwebs\ML\Helper\Data(); 389 $api_helper = new ApiHelper(); 390 391 if ( ! $helper->getIsDynamicPricingEnabled() ) { 392 return false; 393 } 394 // Fetch all pending jobs. 395 $table_name = $wpdb->prefix . 'visualwebs_ml_dynamic_pricing_jobs'; 396 // phpcs:ignore WordPress.DB.DirectDatabaseQuery, phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching 397 $pending_jobs = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE job_status = %s', $table_name, 'processing' ) ); 398 foreach ( $pending_jobs as $job ) { 399 $job_id = $job->job_id; 400 $job_hash = $job->job_hash; 401 $data = array( 402 'job_hash' => $job_hash, 403 ); 404 405 $response = $api_helper->sendDynamicPricingBatchJobStatusRequest( $data ); 406 407 if ( 'done' === $response['status'] && ! empty( $response['job_id'] ) ) { 408 409 if ( 'predict' === $response['type'] ) { 313 function visualwebs_ml_check_dynamic_pricing_batch_jobs() { 314 global $wpdb; 315 $helper = new \Visualwebs\ML\Helper\Data(); 316 $api_helper = new ApiHelper(); 317 318 if ( ! $helper->getIsDynamicPricingEnabled() ) { 319 return false; 320 } 321 // Fetch all pending jobs. 322 $table_name = $wpdb->prefix . 'visualwebs_ml_dynamic_pricing_jobs'; 323 // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 324 $pending_jobs = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE job_status = %s', $table_name, 'processing' ) ); 325 foreach ( $pending_jobs as $job ) { 326 $job_id = $job->job_id; 327 $job_hash = $job->job_hash; 328 $data = array( 329 'job_hash' => $job_hash, 330 ); 331 332 $response = $api_helper->sendDynamicPricingBatchJobStatusRequest( $data ); 333 334 if ( 'done' === $response['status'] && ! empty( $response['job_id'] ) ) { 335 336 if ( 'predict' === $response['type'] ) { 410 337 411 338 $results = $api_helper->downloadDynamicPricingBatchResults( $response['job_id'] ); … … 439 366 440 367 if ( $new_price <= $cogs ) { 441 $min_pct = min( 1, $helper->getDynamicPricingMinProfit() ); 368 // Default minimum profit 10% (config for min_profit was removed, now managed in SaaS) 369 $min_pct = 10; 442 370 $new_price = $cogs * ( 1 + ( $min_pct / 100 ) ); 443 371 } … … 487 415 } 488 416 } 489 );490 417 491 418 /** … … 500 427 function visualwebs_ml_process_queue_item( $job ) { 501 428 global $wpdb; 502 $helper = new Helper(); 503 $pinecone_helper = new PineconeHelper(); 504 $opean_ai_helper = new OpenAiHelper(); 505 $api_helper = new ApiHelper(); 506 $chatbot_helper = new ChatbotHelper(); 507 508 $use_customer_api_keys_enabled = $helper->getUseCustomerApiKeysEnabled(); 509 $pinecone_api_key = $pinecone_helper->getPineconeApiKey(); 510 $pinecone_index_host = $pinecone_helper->getPineconeIndexHost(); 511 $pinecone_namespace = $pinecone_helper->getPineconeNamespace(); 512 $openai_api_key = $opean_ai_helper->getApiKey(); 513 $domain_namespace = $helper->getDomainNamespace(); 514 515 $params = $use_customer_api_keys_enabled ? array( 516 'openai_api_key' => $openai_api_key, 517 'pinecone_api_key' => $pinecone_api_key, 518 'pinecone_index_host' => $pinecone_index_host, 519 'pinecone_namespace' => $pinecone_namespace, 520 ) : array( 521 'pinecone_namespace' => $domain_namespace, 429 $helper = new Helper(); 430 $api_helper = new ApiHelper(); 431 $chatbot_helper = new ChatbotHelper(); 432 433 // Legacy semantic search processing (now managed in SaaS) 434 $params = array( 435 'pinecone_namespace' => $helper->getDomainNamespace(), 522 436 ); 523 437 … … 675 589 ); 676 590 } 591 592 // Feed generation cron job handlers 593 594 add_action('visualwebs_ml_product_feed', function() { 595 require_once plugin_dir_path(__FILE__) . 'feeds/class-product-feed-generator.php'; 596 $generator = new VisualwebsML_ProductFeedGenerator(); 597 $generator->generate(); 598 }); 599 600 add_action('visualwebs_ml_page_feed', function() { 601 require_once plugin_dir_path(__FILE__) . 'feeds/class-page-feed-generator.php'; 602 $generator = new VisualwebsML_PageFeedGenerator(); 603 $generator->generate(); 604 }); 605 606 add_action('visualwebs_ml_sales_feed', function() { 607 require_once plugin_dir_path(__FILE__) . 'feeds/class-sales-feed-generator.php'; 608 $generator = new VisualwebsML_SalesFeedGenerator(); 609 $generator->generate(); 610 }); 611 612 add_action('visualwebs_ml_insights_feed', function() { 613 require_once plugin_dir_path(__FILE__) . 'feeds/class-insights-feed-generator.php'; 614 $generator = new VisualwebsML_InsightsFeedGenerator(); 615 $generator->generate(); 616 }); -
visualwebs-ml/trunk/includes/redux-config.php
r3325171 r3476789 23 23 } 24 24 25 $ opt_name = 'visualwebs_ml_options';26 27 $ args = array(28 'opt_name' => $ opt_name,25 $visualwebs_ml_opt_name = 'visualwebs_ml_options'; 26 27 $visualwebs_ml_args = array( 28 'opt_name' => $visualwebs_ml_opt_name, 29 29 'display_name' => 'Visualwebs AI Cloud Suite Options', 30 30 'menu_type' => 'submenu', … … 57 57 ); 58 58 59 Redux::setArgs( $ opt_name, $args );59 Redux::setArgs( $visualwebs_ml_opt_name, $visualwebs_ml_args ); 60 60 61 61 // Extension Options Group. 62 62 Redux::setSection( 63 $ opt_name,63 $visualwebs_ml_opt_name, 64 64 array( 65 65 'title' => __( 'Extension Options', 'visualwebs-ml' ), … … 82 82 // Visualwebs AI Cloud Suite API Group. 83 83 Redux::setSection( 84 $ opt_name,84 $visualwebs_ml_opt_name, 85 85 array( 86 86 'title' => __( 'API', 'visualwebs-ml' ), … … 107 107 ), 108 108 array( 109 'id' => 'use_customer_api_keys', 110 'type' => 'switch', 111 'title' => __( 'Use your own API Keys', 'visualwebs-ml' ), 112 'default' => false, 113 'on' => 'Enabled', 114 'off' => 'Disabled', 115 'desc' => __( 'Enable only if you want to use your OpenAI and Pinecone keys.', 'visualwebs-ml' ), 116 ), 117 ), 118 ) 119 ); 120 121 // User Widgets Group. 122 Redux::setSection( 123 $opt_name, 124 array( 125 'title' => __( 'User Widgets', 'visualwebs-ml' ), 126 'id' => 'widgets', 127 'desc' => __( 'Settings for User Widgets.', 'visualwebs-ml' ), 128 'icon' => 'el el-th-large', 129 'fields' => array( 130 array( 131 'id' => 'user_widgets', 132 'type' => 'repeater', 133 'group_values' => true, 134 'title' => __( 'Widgets', 'visualwebs-ml' ), 135 'desc' => __( 'Configure user widgets.', 'visualwebs-ml' ), 136 'fields' => array( 137 array( 138 'id' => 'widget_title', 139 'type' => 'text', 140 'title' => __( 'Title', 'visualwebs-ml' ), 141 'default' => '', 142 ), 143 array( 144 'id' => 'widget_instructions', 145 'type' => 'text', 146 'title' => __( 'Instructions', 'visualwebs-ml' ), 147 'default' => '', 148 ), 149 array( 150 'id' => 'widget_render_el', 151 'type' => 'text', 152 'title' => __( 'Render Element', 'visualwebs-ml' ), 153 'default' => '', 154 ), 155 array( 156 'id' => 'widget_source_el', 157 'type' => 'text', 158 'title' => __( 'Source Element', 'visualwebs-ml' ), 159 'default' => '', 160 ), 109 'id' => 'store_id', 110 'type' => 'text', 111 'title' => __( 'Store ID (SaaS UUID)', 'visualwebs-ml' ), 112 'desc' => __( 'UUID from SaaS Dashboard when registering this store.', 'visualwebs-ml' ), 113 'required' => array( 'api_enabled', '=', '1' ), 114 'validate' => 'no_html', 115 ), 116 ), 117 ) 118 ); 119 120 // Workflow Integration Group. 121 Redux::setSection( 122 $visualwebs_ml_opt_name, 123 array( 124 'title' => __( 'Workflow Integration', 'visualwebs-ml' ), 125 'id' => 'workflow', 126 'desc' => __( 'Configure event delivery to SaaS and n8n workflows.', 'visualwebs-ml' ), 127 'icon' => 'el el-random', 128 'fields' => array( 129 array( 130 'id' => 'enable_workflows', 131 'type' => 'switch', 132 'title' => __( 'Enable Workflows', 'visualwebs-ml' ), 133 'default' => false, 134 'on' => 'Enabled', 135 'off' => 'Disabled', 136 'required' => array( 'api_enabled', '=', '1' ), 137 ), 138 array( 139 'id' => 'enabled_events', 140 'type' => 'checkbox', 141 'title' => __( 'Enabled Events', 'visualwebs-ml' ), 142 'options' => array( 143 'woocommerce_new_order' => __( 'Order Created', 'visualwebs-ml' ), 144 'woocommerce_order_status_changed' => __( 'Order Status Changed', 'visualwebs-ml' ), 145 'user_register' => __( 'User Registered', 'visualwebs-ml' ), 146 'woocommerce_product_updated' => __( 'Product Updated', 'visualwebs-ml' ), 161 147 ), 162 'default' => array(), 148 'default' => array( 149 'woocommerce_new_order' => '1', 150 'woocommerce_order_status_changed' => '1', 151 ), 152 'required' => array( 'enable_workflows', '=', '1' ), 153 ), 154 array( 155 'id' => 'anonymize_payload', 156 'type' => 'switch', 157 'title' => __( 'Anonymize Personal Data (GDPR)', 'visualwebs-ml' ), 158 'default' => true, 159 'on' => 'Enabled', 160 'off' => 'Disabled', 161 'desc' => __( 'Redacts personal fields before sending event payloads.', 'visualwebs-ml' ), 162 'required' => array( 'enable_workflows', '=', '1' ), 163 163 ), 164 164 ), … … 168 168 // ChatGPT Group. 169 169 Redux::setSection( 170 $ opt_name,170 $visualwebs_ml_opt_name, 171 171 array( 172 172 'title' => __( 'ChatGPT', 'visualwebs-ml' ), … … 176 176 'fields' => array( 177 177 array( 178 'id' => 'chatgpt_api_key',179 'type' => 'text',180 'title' => __( 'Open AI Api key', 'visualwebs-ml' ),181 'required' => array( 'use_customer_api_keys', '=', '1' ),182 ),183 array(184 178 'id' => 'chatbot_enabled', 185 179 'type' => 'switch', … … 191 185 ), 192 186 array( 193 'id' => 'debug_enabled', 194 'type' => 'switch', 195 'title' => __( 'Enables the Chatbot debug', 'visualwebs-ml' ), 196 'default' => false, 197 'on' => 'Enabled', 198 'off' => 'Disabled', 199 'desc' => __( 'Writes Chatbot information in logs/visualwebs_ml.log', 'visualwebs-ml' ), 200 ), 201 array( 202 'id' => 'chatbot_header_title', 203 'type' => 'text', 204 'default' => 'Support', 205 'title' => __( 'Chatbot header', 'visualwebs-ml' ), 206 'desc' => __( 'The title to show in the header of chat widget, i.e. Support, mysite.com Support..', 'visualwebs-ml' ), 207 ), 208 array( 209 'id' => 'chatbot_logo', 210 'type' => 'media', 211 'title' => __( 'Upload Logo', 'visualwebs-ml' ), 212 'desc' => __( 'Allowed file types: jpg, jpeg, gif, png', 'visualwebs-ml' ), 213 ), 214 array( 215 'id' => 'chatbot_main_background_color', 216 'type' => 'color', 217 'title' => __( 'Color', 'visualwebs-ml' ), 218 'default' => '#ffffff', 219 'required' => array( 'chatbot_enabled', '=', '1' ), 220 'validate' => 'not_empty', 221 ), 222 array( 223 'id' => 'chatbot_context', 224 'type' => 'textarea', 225 'default' => 'Act as E-commerce support team', 226 'title' => __( 'Chatbot context', 'visualwebs-ml' ), 227 'desc' => __( 'Provide clear instructions for the chatbot\'s behavior.', 'visualwebs-ml' ), 228 'required' => array( 'chatbot_enabled', '=', '1' ), 229 'validate' => 'not_empty', 230 ), 231 array( 232 'id' => 'chatbot_offline_message', 233 'type' => 'text', 234 'default' => 'Our support system is OFF now, please try later.', 235 'title' => __( 'Message to show to customers when Bot is Offline.', 'visualwebs-ml' ), 236 'desc' => __( 'Example: Our support system is OFF now, please try later.', 'visualwebs-ml' ), 237 ), 238 array( 239 'id' => 'chatbot_disclaimer_url', 240 'type' => 'text', 241 'title' => __( 'Disclaimer url', 'visualwebs-ml' ), 242 'desc' => __( 'Specify the slug of the disclaimer page.', 'visualwebs-ml' ), 243 ), 244 ), 245 ) 246 ); 247 248 // Semantic Search Group. 249 Redux::setSection( 250 $opt_name, 251 array( 252 'title' => __( 'Semantic Search', 'visualwebs-ml' ), 253 'id' => 'semantic_search', 254 'desc' => __( 'Settings for Semantic Search.', 'visualwebs-ml' ), 255 'icon' => 'el el-search', 256 'fields' => array( 257 array( 258 'id' => 'semantic_search_enabled', 259 'type' => 'switch', 260 'title' => __( 'Enables semantic search', 'visualwebs-ml' ), 261 'default' => true, 262 'on' => 'Enabled', 263 'off' => 'Disabled', 264 'desc' => __( 'Enables the Semantic search. Requires a vector database connection.', 'visualwebs-ml' ), 265 ), 266 array( 267 'id' => 'vector_database_service', 268 'type' => 'text', 269 'title' => __( 'Vector database service', 'visualwebs-ml' ), 270 'required' => array( 'use_customer_api_keys', '=', '1' ), 271 'desc' => __( 'Use a free Pinacone account as starting point. Note you would need a paid subscription for large websites.', 'visualwebs-ml' ), 272 ), 273 array( 274 'id' => 'vector_database_api_key', 275 'type' => 'text', 276 'title' => __( 'Vector database Api key', 'visualwebs-ml' ), 277 'required' => array( 'use_customer_api_keys', '=', '1' ), 278 ), 279 array( 280 'id' => 'vector_database_index_host', 281 'type' => 'text', 282 'title' => __( 'Vector database index host', 'visualwebs-ml' ), 283 'required' => array( 'use_customer_api_keys', '=', '1' ), 284 'desc' => __( 'Find it in Pinecone panel, select the desired index and copy the host.', 'visualwebs-ml' ), 285 ), 286 array( 287 'id' => 'vector_index_namespace', 288 'type' => 'text', 289 'title' => __( 'Vector index namespace', 'visualwebs-ml' ), 290 'required' => array( 'use_customer_api_keys', '=', '1' ), 291 'desc' => __( 'Optional for paid vector accounts. Leave empty as general rule.', 'visualwebs-ml' ), 292 ), 293 array( 294 'id' => 'vector_min_search_score', 295 'type' => 'text', 296 'title' => __( 'Vector min search score', 'visualwebs-ml' ), 297 'default' => '0.75', 298 'desc' => __( 'Start using 0.75. Use lower values to use vector data more frequently.', 'visualwebs-ml' ), 299 ), 300 array( 301 'id' => 'vector_store_dynamic_data', 302 'type' => 'switch', 303 'title' => __( 'Store Product Price and Stock Data', 'visualwebs-ml' ), 304 'default' => false, 305 'on' => 'Enabled', 306 'off' => 'Disabled', 307 'desc' => __( 'Enable if your catalog doesn\'t change frequently. This will add product price and stock data to chatbot knowledge.', 'visualwebs-ml' ), 187 'id' => 'admin_enabled', 188 'type' => 'switch', 189 'title' => __( 'Enable admin widget', 'visualwebs-ml' ), 190 'default' => false, 191 'on' => 'Enabled', 192 'off' => 'Disabled', 193 'desc' => __( 'Enable chatbot widget on admin screens.', 'visualwebs-ml' ), 308 194 ), 309 195 ), … … 313 199 // Dynamic Pricing Group. 314 200 Redux::setSection( 315 $ opt_name,201 $visualwebs_ml_opt_name, 316 202 array( 317 203 'title' => __( 'SmartPricing AI', 'visualwebs-ml' ), … … 344 230 'desc' => __( 'Automatically replaces catalog prices with daily AI predictions.', 'visualwebs-ml' ), 345 231 ), 346 array( 347 'id' => 'dynamic_pricing_min_profit', 348 'type' => 'text', 349 'title' => __( 'Minimum Profit (%)', 'visualwebs-ml' ), 350 'default' => '10', 351 'desc' => __( 'Minimum profit margin as a percentage.', 'visualwebs-ml' ), 352 ), 353 array( 354 'id' => 'dynamic_pricing_max_profit', 355 'type' => 'text', 356 'title' => __( 'Maximum Profit (%)', 'visualwebs-ml' ), 357 'default' => '35', 358 'desc' => __( 'Maximum profit margin as a percentage.', 'visualwebs-ml' ), 359 ), 360 ), 361 ) 362 ); 232 ), 233 ) 234 ); -
visualwebs-ml/trunk/readme.txt
r3357523 r3476789 3 3 Tags: AI, Chatbot, Machine Learning, Spam, ChatGPT 4 4 Requires at least: 6.2 5 Tested up to: 6. 85 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 5. 4.37 Stable tag: 5.5.0 8 8 License: GPLv2 or later 9 9 License URI: https://visualwebs.eu/terms-and-conditions/ … … 80 80 81 81 == Changelog == 82 = 5.2.0 = 83 * Initial release. 84 = 5.3.0 = 85 * Removed the minimum data requirement for Spam and Sentiment Analysis widgets. These can now display results even with just one review, without needing to train a model. 82 = 5.5.0 = 83 * Major Update: Migrated to SaaS architecture (matches Magento module functionality) 84 * New: n8n workflow integration with event system (order events, customer events, product events) 85 * New: Product feed generator with hourly cron job for semantic search sync 86 * New: Page feed generator with daily cron job 87 * New: Sales feed generator for SmartPricing AI training data 88 * New: Insights feed generator (`insights_feed.json`) for dashboard widgets 89 * New: Manual feed generation buttons in admin panel 90 * New: Chatbot admin/backend integration with secure admin endpoint (capability + nonce) 91 * Security: GDPR-compliant payload anonymization for workflows 92 * Improved: Simplified admin interface (dashboard + settings + feed generation) 93 * Deprecated: Database table access methods (use SaaS API instead) 94 * Deprecated: Semantic search and dynamic pricing queue grid classes (use SaaS dashboard) 95 * Performance: Removed legacy cron jobs and unused local processing paths 96 97 = 5.4.3 = 98 * Added English/Spanish translation support for widgets and dashboard. 99 = 5.4.2 = 100 * Improved: Simplified chatbot security to ensure compatibility with caching plugins and language translators. Security checks are now handled by our remote service, which uses a license-based rate limiter for protection. 101 = 5.4.1 = 102 * New feature: Track SmartPricing AI training results together with prediction jobs for improved monitoring and transparency. 86 103 = 5.4.0 = 87 104 * New feature: AI-powered dynamic pricing to help you set the best product sale price and maximize WooCommerce profits. 88 105 * New feature: Bulk append products, categories, or pages for faster AI processing and management. 89 106 * New feature: Attach PDF, DOC, or text files to each semantic search item. Uploaded files are added to the chatbot's knowledge base. 90 = 5.4.1 = 91 * New feature: Track SmartPricing AI training results together with prediction jobs for improved monitoring and transparency. 92 = 5.4.2 = 93 * Improved: Simplified chatbot security to ensure compatibility with caching plugins and language translators. Security checks are now handled by our remote service, which uses a license-based rate limiter for protection. 94 = 5.4.3 = 95 * Added English/Spanish translation support for widgets and dashboard. 107 = 5.3.0 = 108 * Removed the minimum data requirement for Spam and Sentiment Analysis widgets. These can now display results even with just one review, without needing to train a model. 109 = 5.2.0 = 110 * Initial release. 96 111 97 112 == License == -
visualwebs-ml/trunk/templates/ml-dashboard.php
r3319473 r3476789 9 9 * @copyright Visualwebs Spain 10 10 * @license See https://visualwebs.eu/terms-and-conditions/ for license details. 11 * 12 * @since 5.5.0 - Migrated to SaaS architecture 11 13 */ 12 14 … … 19 21 } 20 22 21 use Visualwebs\ML\Block\Adminhtml\Dashboard;22 23 use Visualwebs\ML\Helper\Data as Helper; 23 24 24 $ dashboard = new Dashboard();25 $visualwebs_ml_helper = new Helper(); 25 26 26 if ( ! $ dashboard->getIsVisualwebsMlServicesEnabled() ) {27 if ( ! $visualwebs_ml_helper->getIsVisualwebsMlServicesEnabled() ) { 27 28 echo '<div class="notice notice-warning"><p>'; 28 29 echo esc_html__( 'Please purchase a subscription ', 'visualwebs-ml' ); … … 35 36 } 36 37 37 if ( isset( $_GET['clear_cache'] ) ) { 38 check_admin_referer( 'clear_cache_action' ); 39 if ( true === (bool) $_GET['clear_cache'] ) { 40 delete_transient( Helper::CACHE_TRANSIENT ); 41 } 42 } 38 $visualwebs_ml_dashboard_url = 'https://saas.visualwebs.eu/dashboard'; 39 $visualwebs_ml_api_key = $visualwebs_ml_helper->getApiKey(); 40 $visualwebs_ml_store_id = $visualwebs_ml_helper->getStoreId(); 41 $visualwebs_ml_workflows_enabled = $visualwebs_ml_helper->getConfig('enable_workflows', false); 42 ?> 43 44 <div class="wrap"> 45 <h1><?php esc_html_e('Visualwebs AI Cloud Suite', 'visualwebs-ml'); ?></h1> 46 47 <div class="card"> 48 <h2><?php esc_html_e('SaaS Dashboard', 'visualwebs-ml'); ?></h2> 49 <p><?php esc_html_e('All AI features are managed from the centralized SaaS dashboard:', 'visualwebs-ml'); ?></p> 50 <ul> 51 <li><?php esc_html_e('Semantic Search Queue Management', 'visualwebs-ml'); ?></li> 52 <li><?php esc_html_e('SmartPricing AI Jobs & Training', 'visualwebs-ml'); ?></li> 53 <li><?php esc_html_e('n8n Workflow Monitoring', 'visualwebs-ml'); ?></li> 54 <li><?php esc_html_e('Analytics & Insights', 'visualwebs-ml'); ?></li> 55 </ul> 56 <p> 57 <a href="<?php echo esc_url($visualwebs_ml_dashboard_url); ?>" target="_blank" class="button button-primary button-hero"> 58 <?php esc_html_e('Open SaaS Dashboard', 'visualwebs-ml'); ?> → 59 </a> 60 </p> 61 </div> 62 63 <div class="card"> 64 <h2><?php esc_html_e('Configuration Status', 'visualwebs-ml'); ?></h2> 65 <table class="widefat"> 66 <tbody> 67 <tr> 68 <td><strong><?php esc_html_e('API Key', 'visualwebs-ml'); ?></strong></td> 69 <td> 70 <?php if ($visualwebs_ml_api_key): ?> 71 <span style="color: #46b450;">✓ <?php esc_html_e('Configured', 'visualwebs-ml'); ?></span> 72 <?php else: ?> 73 <span style="color: #dc3232;">✗ <?php esc_html_e('Missing', 'visualwebs-ml'); ?></span> 74 <?php endif; ?> 75 </td> 76 </tr> 77 <tr> 78 <td><strong><?php esc_html_e('Store ID', 'visualwebs-ml'); ?></strong></td> 79 <td> 80 <?php if ($visualwebs_ml_store_id): ?> 81 <span style="color: #46b450;">✓ <?php echo esc_html($visualwebs_ml_store_id); ?></span> 82 <?php else: ?> 83 <span style="color: #dc3232;">✗ <?php esc_html_e('Missing', 'visualwebs-ml'); ?></span> 84 <?php endif; ?> 85 </td> 86 </tr> 87 <tr> 88 <td><strong><?php esc_html_e('n8n Workflows', 'visualwebs-ml'); ?></strong></td> 89 <td> 90 <?php if ($visualwebs_ml_workflows_enabled): ?> 91 <span style="color: #46b450;">✓ <?php esc_html_e('Enabled', 'visualwebs-ml'); ?></span> 92 <?php else: ?> 93 <span style="color: #999;">✗ <?php esc_html_e('Disabled', 'visualwebs-ml'); ?></span> 94 <?php endif; ?> 95 </td> 96 </tr> 97 <tr> 98 <td><strong><?php esc_html_e('SmartPricing AI', 'visualwebs-ml'); ?></strong></td> 99 <td> 100 <?php if ($visualwebs_ml_helper->getIsDynamicPricingEnabled()): ?> 101 <span style="color: #46b450;">✓ <?php esc_html_e('Enabled', 'visualwebs-ml'); ?></span> 102 <?php else: ?> 103 <span style="color: #999;">✗ <?php esc_html_e('Disabled', 'visualwebs-ml'); ?></span> 104 <?php endif; ?> 105 </td> 106 </tr> 107 <tr> 108 <td><strong><?php esc_html_e('Chatbot Widget', 'visualwebs-ml'); ?></strong></td> 109 <td> 110 <?php if ($visualwebs_ml_helper->getConfig('chatbot_enabled', false)): ?> 111 <span style="color: #46b450;">✓ <?php esc_html_e('Enabled', 'visualwebs-ml'); ?></span> 112 <?php else: ?> 113 <span style="color: #999;">✗ <?php esc_html_e('Disabled', 'visualwebs-ml'); ?></span> 114 <?php endif; ?> 115 </td> 116 </tr> 117 </tbody> 118 </table> 119 </div> 120 121 <div class="card"> 122 <h2><?php esc_html_e('Feed Synchronization', 'visualwebs-ml'); ?></h2> 123 <p><?php esc_html_e('Automatic feed generation is configured:', 'visualwebs-ml'); ?></p> 124 <ul> 125 <li><strong><?php esc_html_e('Product Feed', 'visualwebs-ml'); ?>:</strong> <?php esc_html_e('Hourly', 'visualwebs-ml'); ?></li> 126 <li><strong><?php esc_html_e('Page Feed', 'visualwebs-ml'); ?>:</strong> <?php esc_html_e('Daily at 2 AM', 'visualwebs-ml'); ?></li> 127 <li><strong><?php esc_html_e('Sales Feed', 'visualwebs-ml'); ?>:</strong> <?php esc_html_e('Daily at 2 AM', 'visualwebs-ml'); ?></li> 128 </ul> 129 <p> 130 <a href="<?php echo esc_url(admin_url('admin.php?page=visualwebs-ml-feeds')); ?>" class="button"> 131 <?php esc_html_e('Manage Feeds', 'visualwebs-ml'); ?> 132 </a> 133 </p> 134 </div> 135 136 <div class="card"> 137 <h2><?php esc_html_e('Quick Links', 'visualwebs-ml'); ?></h2> 138 <p> 139 <a href="<?php echo esc_url(admin_url('admin.php?page=visualwebs-ml-manage-options')); ?>" class="button"> 140 <?php esc_html_e('Settings', 'visualwebs-ml'); ?> 141 </a> 142 <a href="<?php echo esc_url(admin_url('admin.php?page=visualwebs-ml-feeds')); ?>" class="button"> 143 <?php esc_html_e('Feed Generation', 'visualwebs-ml'); ?> 144 </a> 145 <a href="https://visualwebs.eu/docs/plugin/ai-cloud-suite/" target="_blank" class="button"> 146 <?php esc_html_e('Documentation', 'visualwebs-ml'); ?> 147 </a> 148 </p> 149 </div> 150 </div> -
visualwebs-ml/trunk/vendor/autoload.php
r3303071 r3476789 15 15 } 16 16 } 17 trigger_error( 18 $err, 19 E_USER_ERROR 20 ); 17 throw new RuntimeException($err); 21 18 } 22 19 -
visualwebs-ml/trunk/vendor/composer/autoload_static.php
r3319473 r3476789 12 12 13 13 public static $prefixLengthsPsr4 = array ( 14 'V' => 14 'V' => 15 15 array ( 16 16 'Visualwebs\\ML\\' => 14, 17 17 ), 18 'S' => 18 'S' => 19 19 array ( 20 20 'Symfony\\Polyfill\\Mbstring\\' => 26, 21 21 ), 22 'P' => 22 'P' => 23 23 array ( 24 24 'PhpOffice\\PhpWord\\' => 18, … … 28 28 29 29 public static $prefixDirsPsr4 = array ( 30 'Visualwebs\\ML\\' => 30 'Visualwebs\\ML\\' => 31 31 array ( 32 32 0 => __DIR__ . '/..' . '/visualwebs-ml', 33 33 ), 34 'Symfony\\Polyfill\\Mbstring\\' => 34 'Symfony\\Polyfill\\Mbstring\\' => 35 35 array ( 36 36 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', 37 37 ), 38 'PhpOffice\\PhpWord\\' => 38 'PhpOffice\\PhpWord\\' => 39 39 array ( 40 40 0 => __DIR__ . '/..' . '/phpoffice/phpword/src/PhpWord', 41 41 ), 42 'PhpOffice\\Math\\' => 42 'PhpOffice\\Math\\' => 43 43 array ( 44 44 0 => __DIR__ . '/..' . '/phpoffice/math/src/Math', … … 47 47 48 48 public static $prefixesPsr0 = array ( 49 'S' => 49 'S' => 50 50 array ( 51 'Smalot\\PdfParser\\' => 51 'Smalot\\PdfParser\\' => 52 52 array ( 53 53 0 => __DIR__ . '/..' . '/smalot/pdfparser/src', -
visualwebs-ml/trunk/vendor/composer/platform_check.php
r3319484 r3476789 20 20 } 21 21 } 22 trigger_error( 23 'Composer detected issues in your platform: ' . implode(' ', $issues), 24 E_USER_ERROR 22 throw new \RuntimeException( 23 'Composer detected issues in your platform: ' . implode(' ', $issues) 25 24 ); 26 25 } -
visualwebs-ml/trunk/vendor/visualwebs-ml/Activator.php
r3325281 r3476789 3 3 namespace Visualwebs\ML; 4 4 5 /** 6 * Plugin Activator 7 * 8 * @package Visualwebs\ML 9 * @since 5.5.0 10 * 11 * Note: Database tables removed in v5.5.0 - SaaS architecture migration. 12 * All data is now managed centrally by the SaaS platform. 13 * Configuration is stored in wp_options table. 14 */ 5 15 class Activator 6 16 { 17 /** 18 * Plugin activation hook 19 * 20 * @since 5.5.0 No database tables created - SaaS manages all data 21 */ 7 22 public static function activate() 8 23 { 9 global $wpdb; 10 11 $sematic_queue_table_name = $wpdb->prefix . 'visualwebs_ml_semantic_queue'; 12 $charset_collate = $wpdb->get_charset_collate(); 13 14 $sql1 = "CREATE TABLE $sematic_queue_table_name ( 15 job_id int(11) NOT NULL AUTO_INCREMENT COMMENT 'Job Id', 16 job_object_entity_id int(11) NOT NULL COMMENT 'Job Object Entity Id', 17 job_object_type varchar(50) NOT NULL COMMENT 'Job Object Type', 18 job_object_name varchar(255) DEFAULT NULL COMMENT 'Job Object Name', 19 job_object_ref varchar(255) DEFAULT NULL COMMENT 'Job Object Ref', 20 job_object_content text COMMENT 'Job Object Content', 21 job_object_content_lock smallint(6) NOT NULL DEFAULT '0' COMMENT 'Job Object Content Lock', 22 job_status smallint(6) NOT NULL DEFAULT '0' COMMENT 'Status', 23 job_sync_status smallint(6) NOT NULL DEFAULT '0' COMMENT 'Sync Status', 24 created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Date and time of job creation', 25 updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Date and time of job update', 26 job_errors varchar(255) DEFAULT NULL COMMENT 'Job Errors', 27 job_object_files_content text COMMENT 'Job Object Files Content', 28 PRIMARY KEY (job_id), 29 UNIQUE KEY ML_SEMANTIC_QUEUE_JOB_OBJECT_TYPE_JOB_OBJECT_ENTITY_ID (job_object_type, job_object_entity_id) 30 ) $charset_collate;"; 31 32 $dynamic_pricing_jobs_table_name = $wpdb->prefix . 'visualwebs_ml_dynamic_pricing_jobs'; 33 34 $sql2 = "CREATE TABLE $dynamic_pricing_jobs_table_name ( 35 job_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, 36 job_hash VARCHAR(128) NOT NULL, 37 job_type VARCHAR(32) NOT NULL DEFAULT 'train', 38 job_status VARCHAR(20) NOT NULL DEFAULT 'pending', 39 created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Date and time of job creation', 40 updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Date and time of job update', 41 job_data LONGTEXT, 42 job_response LONGTEXT, 43 PRIMARY KEY (job_id), 44 UNIQUE KEY job_hash (job_hash) 45 ) $charset_collate;"; 46 47 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 48 dbDelta($sql1); 49 dbDelta($sql2); 24 // No database tables needed - SaaS architecture 25 // Configuration is stored in wp_options via Redux Framework 26 // Feeds are generated as JSON files in wp-content/uploads/visualwebs-ai/ 27 28 // Ensure upload directory exists 29 $upload_dir = wp_upload_dir(); 30 $visualwebs_dir = $upload_dir['basedir'] . '/visualwebs-ai'; 31 32 if (!file_exists($visualwebs_dir)) { 33 wp_mkdir_p($visualwebs_dir); 34 } 35 36 // Set default options if not present 37 $options = get_option('visualwebs_ml_options'); 38 39 if ($options === false) { 40 $default_options = array( 41 'enabled' => false, 42 'api_enabled' => false, 43 'chatbot_enabled' => false, 44 'enable_workflows' => false, 45 'anonymize_payload' => false, 46 ); 47 48 add_option('visualwebs_ml_options', $default_options); 49 } 50 51 // Flush rewrite rules for REST API endpoints 52 flush_rewrite_rules(); 50 53 } 51 54 } -
visualwebs-ml/trunk/vendor/visualwebs-ml/Admin.php
r3319473 r3476789 25 25 { 26 26 add_action('admin_menu', [__CLASS__, 'add_admin_menu']); 27 add_action('admin_footer', [__CLASS__, ' widget_page']);27 add_action('admin_footer', [__CLASS__, 'render_chatbot_widget']); 28 28 add_action('wp_ajax_visualwebs_ml_search_entity', [__CLASS__, 'visualwebs_ml_search_entity']); 29 29 if (self::get_helper()->getIsDynamicPricingEnabled()) { … … 62 62 add_submenu_page( 63 63 'visualwebs-ml', 64 'Semantic Search',65 'Semantic Search',66 'manage_options',67 'visualwebs-ml-semantic-queue',68 [__CLASS__, 'semantic_search_queue_page']69 );70 71 add_submenu_page(72 null,73 'Add New Search Item',74 'Add New Search Item',75 'manage_options',76 'visualwebs-ml-semantic-add',77 [__CLASS__, 'semantic_search_add_page']78 );79 80 add_submenu_page(81 null,82 'Edit Search Item',83 'Edit Search Item',84 'manage_options',85 'visualwebs-ml-semantic-edit',86 [__CLASS__, 'semantic_search_edit_page']87 );88 89 add_submenu_page(90 'visualwebs-ml',91 'SmartPricing AI',92 'SmartPricing AI',93 'manage_options',94 'visualwebs-ml-dynamic-pricing-queue',95 [__CLASS__, 'dynamic_pricing_queue_page']96 );97 98 add_submenu_page(99 null,100 'View Job Item',101 'View Job Item',102 'manage_options',103 'visualwebs-ml-dynamic-pricing-view',104 [__CLASS__, 'dynamic_pricing_queue_view_page']105 );106 107 add_submenu_page(108 null,109 'Train',110 'Train',111 'manage_options',112 'visualwebs-ml-dynamic-pricing-train',113 [__CLASS__, 'dynamic_pricing_train_page']114 );115 116 add_submenu_page(117 null,118 'Predict',119 'Predict',120 'manage_options',121 'visualwebs-ml-dynamic-pricing-predict',122 [__CLASS__, 'dynamic_pricing_predict_page']123 );124 125 add_submenu_page(126 'visualwebs-ml',127 64 'Settings', 128 65 'Settings', … … 130 67 'visualwebs-ml-manage-options', 131 68 [__CLASS__, 'settings_page'] 69 ); 70 71 add_submenu_page( 72 'visualwebs-ml', 73 'Feed Generation', 74 'Feed Generation', 75 'manage_options', 76 'visualwebs-ml-feeds', 77 [__CLASS__, 'feeds_page'] 132 78 ); 133 79 … … 264 210 public static function dashboard_page() 265 211 { 266 include(plugin_dir_path(__FILE__) . '../../templates/ml-widgets.php');267 212 include(plugin_dir_path(__FILE__) . '../../templates/ml-dashboard.php'); 268 213 } 269 214 270 public static function widget_page() 271 { 272 include(plugin_dir_path(__FILE__) . '../../templates/ml-widgets.php'); 273 } 274 275 public static function semantic_search_queue_page() 276 { 277 include plugin_dir_path(__FILE__) . '../../templates/semantic-search-queue.php'; 278 } 279 280 public static function semantic_search_add_page() 281 { 282 include plugin_dir_path(__FILE__) . '../../templates/semantic-search-add.php'; 283 } 284 285 public static function semantic_search_edit_page() 286 { 287 include plugin_dir_path(__FILE__) . '../../templates/semantic-search-edit.php'; 288 } 289 290 public static function dynamic_pricing_queue_page() 291 { 292 include plugin_dir_path(__FILE__) . '../../templates/dynamic-pricing-queue.php'; 293 } 294 295 public static function dynamic_pricing_queue_view_page() 296 { 297 include plugin_dir_path(__FILE__) . '../../templates/dynamic-pricing-view.php'; 298 } 299 300 public static function dynamic_pricing_train_page() 301 { 302 include plugin_dir_path(__FILE__) . '../../templates/dynamic-pricing-train.php'; 303 } 304 305 public static function dynamic_pricing_predict_page() 306 { 307 include plugin_dir_path(__FILE__) . '../../templates/dynamic-pricing-predict.php'; 215 /** 216 * Render chatbot widget in admin footer. 217 * 218 * @since 5.5.0 219 */ 220 public static function render_chatbot_widget() 221 { 222 require plugin_dir_path(__FILE__) . '../../templates/n8n-chatbot-widget.php'; 223 } 224 225 public static function settings_page() 226 { 227 // Redux Framework handles this page 228 } 229 230 public static function feeds_page() 231 { 232 require_once plugin_dir_path(__FILE__) . '../../includes/feeds/class-product-feed-generator.php'; 233 require_once plugin_dir_path(__FILE__) . '../../includes/feeds/class-page-feed-generator.php'; 234 require_once plugin_dir_path(__FILE__) . '../../includes/feeds/class-sales-feed-generator.php'; 235 require_once plugin_dir_path(__FILE__) . '../../includes/feeds/class-insights-feed-generator.php'; 236 237 $message = ''; 238 $messageType = ''; 239 240 if (isset($_POST['generate_products']) && check_admin_referer('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce')) { 241 $generator = new \VisualwebsML_ProductFeedGenerator(); 242 $url = $generator->generate(); 243 if ($url) { 244 $message = sprintf(__('Product feed generated successfully: %s', 'visualwebs-ml'), $url); 245 $messageType = 'success'; 246 } else { 247 $message = __('Failed to generate product feed', 'visualwebs-ml'); 248 $messageType = 'error'; 249 } 250 } 251 252 if (isset($_POST['generate_pages']) && check_admin_referer('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce')) { 253 $generator = new \VisualwebsML_PageFeedGenerator(); 254 $url = $generator->generate(); 255 if ($url) { 256 $message = sprintf(__('Page feed generated successfully: %s', 'visualwebs-ml'), $url); 257 $messageType = 'success'; 258 } else { 259 $message = __('Failed to generate page feed', 'visualwebs-ml'); 260 $messageType = 'error'; 261 } 262 } 263 264 if (isset($_POST['generate_sales']) && check_admin_referer('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce')) { 265 $generator = new \VisualwebsML_SalesFeedGenerator(); 266 $url = $generator->generate(); 267 if ($url) { 268 $message = sprintf(__('Sales feed generated successfully: %s', 'visualwebs-ml'), $url); 269 $messageType = 'success'; 270 } else { 271 $message = __('Failed to generate sales feed', 'visualwebs-ml'); 272 $messageType = 'error'; 273 } 274 } 275 276 if (isset($_POST['generate_insights']) && check_admin_referer('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce')) { 277 $generator = new \VisualwebsML_InsightsFeedGenerator(); 278 $url = $generator->generate(); 279 if ($url) { 280 $message = sprintf(__('Insights feed generated successfully: %s', 'visualwebs-ml'), $url); 281 $messageType = 'success'; 282 } else { 283 $message = __('Failed to generate insights feed', 'visualwebs-ml'); 284 $messageType = 'error'; 285 } 286 } 287 288 ?> 289 <div class="wrap"> 290 <h1><?php esc_html_e('Feed Generation', 'visualwebs-ml'); ?></h1> 291 292 <?php if ($message): ?> 293 <div class="notice notice-<?php echo esc_attr($messageType); ?> is-dismissible"> 294 <p><?php echo esc_html($message); ?></p> 295 </div> 296 <?php endif; ?> 297 298 <p><?php esc_html_e('Manually trigger feed generation. Feeds are also generated automatically via cron jobs.', 'visualwebs-ml'); ?></p> 299 300 <div class="card"> 301 <h2><?php esc_html_e('Product Feed', 'visualwebs-ml'); ?></h2> 302 <p><?php esc_html_e('Generates JSON feed of all products for semantic search synchronization. Auto-generated hourly.', 'visualwebs-ml'); ?></p> 303 <form method="post"> 304 <?php wp_nonce_field('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce'); ?> 305 <button type="submit" name="generate_products" class="button button-primary"> 306 <?php esc_html_e('Generate Product Feed', 'visualwebs-ml'); ?> 307 </button> 308 </form> 309 </div> 310 311 <div class="card"> 312 <h2><?php esc_html_e('Page Feed', 'visualwebs-ml'); ?></h2> 313 <p><?php esc_html_e('Generates JSON feed of all pages and posts for content search. Auto-generated daily at 2 AM.', 'visualwebs-ml'); ?></p> 314 <form method="post"> 315 <?php wp_nonce_field('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce'); ?> 316 <button type="submit" name="generate_pages" class="button button-primary"> 317 <?php esc_html_e('Generate Page Feed', 'visualwebs-ml'); ?> 318 </button> 319 </form> 320 </div> 321 322 <div class="card"> 323 <h2><?php esc_html_e('Sales Feed', 'visualwebs-ml'); ?></h2> 324 <p><?php esc_html_e('Generates JSON feed of sales data for SmartPricing AI training. Auto-generated daily at 2 AM.', 'visualwebs-ml'); ?></p> 325 <form method="post"> 326 <?php wp_nonce_field('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce'); ?> 327 <button type="submit" name="generate_sales" class="button button-primary"> 328 <?php esc_html_e('Generate Sales Feed', 'visualwebs-ml'); ?> 329 </button> 330 </form> 331 </div> 332 333 <div class="card"> 334 <h2><?php esc_html_e('Insights Feed', 'visualwebs-ml'); ?></h2> 335 <p><?php esc_html_e('Generates JSON feed of dashboard insights (sales, bestsellers, reviews, registrations) for SaaS widgets. Auto-generated daily at 3 AM.', 'visualwebs-ml'); ?></p> 336 <form method="post"> 337 <?php wp_nonce_field('visualwebs_ml_feeds', 'visualwebs_ml_feeds_nonce'); ?> 338 <button type="submit" name="generate_insights" class="button button-primary"> 339 <?php esc_html_e('Generate Insights Feed', 'visualwebs-ml'); ?> 340 </button> 341 </form> 342 </div> 343 344 <div class="card"> 345 <h2><?php esc_html_e('Feed Files', 'visualwebs-ml'); ?></h2> 346 <p><?php esc_html_e('Generated feeds are stored in:', 'visualwebs-ml'); ?> <code><?php echo esc_html(wp_upload_dir()['baseurl'] . '/visualwebs-ml/'); ?></code></p> 347 <ul> 348 <li><strong>product_feed.json</strong> - Product catalog</li> 349 <li><strong>page_feed.json</strong> - Pages and posts</li> 350 <li><strong>sales_feed.json</strong> - Sales data for SmartPricing</li> 351 <li><strong>insights_feed.json</strong> - Dashboard insights and statistics</li> 352 </ul> 353 </div> 354 </div> 355 <?php 308 356 } 309 357 -
visualwebs-ml/trunk/vendor/visualwebs-ml/Block/Adminhtml/Dashboard.php
r3337157 r3476789 320 320 * Retrieve the existing widgets for the dashboard. 321 321 * 322 * @return array An array of user widgets .322 * @return array An array of user widgets (empty - config removed). 323 323 */ 324 324 public function getUserWidgets() 325 325 { 326 return $this->helper->getUserWidgets(); 326 // User widgets config removed (now managed in SaaS) 327 return []; 327 328 } 328 329 -
visualwebs-ml/trunk/vendor/visualwebs-ml/Block/ChatWidget.php
r3337157 r3476789 40 40 41 41 /** 42 * Retrieve the URL for the admin chatbot proxy API. 43 * 44 * @return string 45 */ 46 public function getAdminChatApiUrl() 47 { 48 return $this->helper->getAdminChatApiUrl(); 49 } 50 51 /** 42 52 * Check if the chatbot is enabled. 43 53 * … … 47 57 { 48 58 return $this->helper->getIsChatbotEnabled(); 59 } 60 61 /** 62 * Check if the admin chatbot is enabled. 63 * 64 * @return bool 65 */ 66 public function getIsAdminChatbotEnabled() 67 { 68 return $this->helper->getIsAdminChatbotEnabled(); 49 69 } 50 70 … … 235 255 return $this->helper->getCurrentFrontendLanguageShort(); 236 256 } 257 258 /** 259 * Get chatbot namespace. 260 * 261 * @return string 262 */ 263 public function getChatbotNamespace() 264 { 265 return $this->helper->getChatbotNamespace(); 266 } 267 268 /** 269 * Get chatbot store ID. 270 * 271 * @return string 272 */ 273 public function getChatbotStoreId() 274 { 275 return $this->helper->getChatbotStoreId(); 276 } 277 278 /** 279 * Get "Powered by" text. 280 * 281 * @return string 282 */ 283 public function getChatbotPoweredByText() 284 { 285 return $this->helper->getChatbotPoweredByText(); 286 } 287 288 /** 289 * Get "Powered by" link. 290 * 291 * @return string 292 */ 293 public function getChatbotPoweredByLink() 294 { 295 return $this->helper->getChatbotPoweredByLink(); 296 } 297 298 /** 299 * Get chatbot assets base URL. 300 * 301 * @return string 302 */ 303 public function getChatbotAssetsBaseUrl() 304 { 305 return $this->helper->getChatbotAssetsBaseUrl(); 306 } 307 308 /** 309 * Get conversation unique session ID. 310 * 311 * @return string 312 */ 313 public function getConversationUniqueSessionId() 314 { 315 return $this->helper->getConversationUniqueSessionId(); 316 } 237 317 } -
visualwebs-ml/trunk/vendor/visualwebs-ml/Helper/Chatbot.php
r3319473 r3476789 10 10 class Chatbot extends \Visualwebs\ML\Helper\Data 11 11 { 12 private const SAAS_API_BASE_URL = 'https://saas.visualwebs.eu/api/v1'; 13 private const SAAS_CHATBOT_CONFIG_PATH = '/chatbot/public/config/'; 14 private const SAAS_CHATBOT_CACHE_TTL = DAY_IN_SECONDS; 15 private const SAAS_CHATBOT_ASSETS_BASE_URL = 'https://saas.visualwebs.eu/assets/chatbot/v1'; 16 12 17 public const CHATBOT_DEFAULT_HEADER_TITLE = 'Support'; 13 18 public const CHATBOT_DEFAULT_OFFLINE_MESSAGE = 'Our support system is OFF now, please try later.'; … … 23 28 $isChatbotEnabled = $this->getConfig('chatbot_enabled', false); 24 29 $isVisualwebsMlServicesEnabled = $this->getIsVisualwebsMlServicesEnabled(); 25 return $isChatbotEnabled && $isVisualwebsMlServicesEnabled; 30 $chatApiUrl = $this->getChatApiUrl(); 31 32 return $isChatbotEnabled && $isVisualwebsMlServicesEnabled && !empty($chatApiUrl); 33 } 34 35 /** 36 * Check if the admin chatbot is enabled. 37 * 38 * @return bool 39 */ 40 public function getIsAdminChatbotEnabled() 41 { 42 $isAdminEnabled = $this->getConfig('admin_enabled', false); 43 $isVisualwebsMlServicesEnabled = $this->getIsVisualwebsMlServicesEnabled(); 44 $chatApiUrl = $this->getChatApiUrl(); 45 46 return $isAdminEnabled && $isVisualwebsMlServicesEnabled && !empty($chatApiUrl); 26 47 } 27 48 … … 117 138 public function getChatbotMainBackgroundColor() 118 139 { 119 $ backgroundColor = $this->getConfig('chatbot_main_background_color', true);120 return $ backgroundColor ?: "#000";140 $saasColor = $this->getSaasWidgetConfigValue('color'); 141 return $saasColor ? (string)$saasColor : "#000"; 121 142 } 122 143 … … 128 149 public function getChatbotDisclaimerUrl() 129 150 { 130 $ disclaimerUrl = $this->getConfig('chatbot_disclaimer_url', '');131 return $ disclaimerUrl ? get_page_link(get_page_by_path($disclaimerUrl)) : get_page_link(get_page_by_path('about-us'));151 $saasDisclaimer = $this->getSaasWidgetConfigValue('disclaimer_url'); 152 return $saasDisclaimer ? (string)$saasDisclaimer : ''; 132 153 } 133 154 … … 139 160 public function getChatbotContext() 140 161 { 141 $chatBotContext = $this->getChatbotContextValue(); 142 return $chatBotContext ? trim($chatBotContext) : __('Act as E-commerce expert', 'visualwebs-ml'); 143 } 144 145 /** 146 * Retrieve the context value for the chatbot in config. 147 * 148 * @return mixed The user defined context value for the chatbot. 149 */ 150 public function getChatbotContextValue() 151 { 152 $chatBotContext = $this->getConfig('chatbot_context', ''); 153 return $chatBotContext; 162 $saasPrompt = $this->getSaasWidgetConfigValue('prompt'); 163 return $saasPrompt ? trim((string)$saasPrompt) : __('Act as E-commerce expert', 'visualwebs-ml'); 154 164 } 155 165 … … 161 171 public function getChatbotHeaderTitle() 162 172 { 163 $ chatBotHeadTitle = $this->getConfig('chatbot_header_title', '');173 $saasTitle = $this->getSaasWidgetConfigValue('title'); 164 174 $defaultChatBotHeadTitle = self::CHATBOT_DEFAULT_HEADER_TITLE; 165 return $ chatBotHeadTitle ? trim($chatBotHeadTitle) : $defaultChatBotHeadTitle;175 return $saasTitle ? trim((string)$saasTitle) : __($defaultChatBotHeadTitle, 'visualwebs-ml'); 166 176 } 167 177 … … 173 183 public function getChatbotOfflineMessage() 174 184 { 175 $ chatBotOfflineMessage = $this->getConfig('chatbot_offline_message', '');185 $saasOfflineMessage = $this->getSaasWidgetConfigValue('offline_message'); 176 186 $defaultChatBotOfflineMessage = self::CHATBOT_DEFAULT_OFFLINE_MESSAGE; 177 return $ chatBotOfflineMessage ? trim($chatBotOfflineMessage) : $defaultChatBotOfflineMessage;187 return $saasOfflineMessage ? trim((string)$saasOfflineMessage) : __($defaultChatBotOfflineMessage, 'visualwebs-ml'); 178 188 } 179 189 … … 185 195 public function getChatbotLogoUrl() 186 196 { 187 if ($imagePath = $this->getConfig('chatbot_logo')) { 188 if (isset($imagePath['url'])) { 189 return $imagePath['url']; 190 } 191 } 192 193 return ''; 197 $saasLogo = $this->getSaasWidgetConfigValue('logo'); 198 return $saasLogo ? (string)$saasLogo : ''; 199 } 200 201 /** 202 * Retrieve SaaS chatbot webhook URL. 203 * 204 * @return string 205 */ 206 public function getChatApiUrl() 207 { 208 $webhookUrl = $this->getSaasChatbotConfigValue('n8n_webhook_url'); 209 return $webhookUrl ? esc_url_raw((string) $webhookUrl) : ''; 210 } 211 212 /** 213 * Retrieve admin chatbot proxy endpoint (secured local REST endpoint). 214 * 215 * @return string 216 */ 217 public function getAdminChatApiUrl() 218 { 219 $nonce = wp_create_nonce('visualwebs_ml_admin_chat'); 220 return esc_url_raw(rest_url('visualwebs-ml/v1/chat-api/admin-call?_vwml_nonce=' . rawurlencode($nonce))); 194 221 } 195 222 … … 225 252 } 226 253 254 /** 255 * Returns raw SaaS chatbot config payload (cached). 256 * 257 * @return array|null 258 */ 259 private function getSaasChatbotConfig() 260 { 261 $storeId = $this->getStoreId(); 262 if (empty($storeId)) { 263 return null; 264 } 265 266 $cacheKey = 'vwml_chatbot_config_' . md5($storeId); 267 $cached = get_transient($cacheKey); 268 if (is_array($cached)) { 269 return $cached; 270 } 271 272 $url = self::SAAS_API_BASE_URL . self::SAAS_CHATBOT_CONFIG_PATH . rawurlencode($storeId); 273 $headers = array('Accept' => 'application/json'); 274 $apiKey = $this->getApiKey(); 275 if (!empty($apiKey)) { 276 $headers['x-api-key'] = $apiKey; 277 } 278 279 $response = wp_remote_get( 280 $url, 281 array( 282 'headers' => $headers, 283 'timeout' => 8, 284 ) 285 ); 286 287 if (is_wp_error($response)) { 288 $this->debug('[Visualwebs ML] Failed to fetch SaaS chatbot config: ' . $response->get_error_message()); 289 return null; 290 } 291 292 $status = (int) wp_remote_retrieve_response_code($response); 293 if ($status !== 200) { 294 $this->debug('[Visualwebs ML] Failed to fetch SaaS chatbot config. HTTP status: ' . $status); 295 return null; 296 } 297 298 $body = wp_remote_retrieve_body($response); 299 $decoded = json_decode($body, true); 300 if (!is_array($decoded)) { 301 return null; 302 } 303 304 set_transient($cacheKey, $decoded, self::SAAS_CHATBOT_CACHE_TTL); 305 306 return $decoded; 307 } 308 309 /** 310 * Returns a top-level key from SaaS chatbot config payload. 311 * 312 * @param string $key 313 * @return mixed|null 314 */ 315 private function getSaasChatbotConfigValue($key) 316 { 317 $response = $this->getSaasChatbotConfig(); 318 if (!$response || !isset($response['config']) || !is_array($response['config'])) { 319 return null; 320 } 321 322 return array_key_exists($key, $response['config']) ? $response['config'][$key] : null; 323 } 324 325 /** 326 * Returns a widget_config key from SaaS config. 327 * 328 * @param string $key 329 * @return mixed|null 330 */ 331 private function getSaasWidgetConfigValue($key) 332 { 333 $response = $this->getSaasChatbotConfig(); 334 if (!$response || !isset($response['config']) || !is_array($response['config'])) { 335 return null; 336 } 337 338 $config = $response['config']; 339 if (!isset($config['widget_config'])) { 340 return null; 341 } 342 343 $widgetConfig = $config['widget_config']; 344 if (is_string($widgetConfig)) { 345 $decoded = json_decode($widgetConfig, true); 346 $widgetConfig = is_array($decoded) ? $decoded : array(); 347 } 348 349 return isset($widgetConfig[$key]) ? $widgetConfig[$key] : null; 350 } 351 352 /** 353 * Get chatbot namespace (for multi-bot identification). 354 * 355 * @return string 356 */ 357 public function getChatbotNamespace() 358 { 359 return $this->getSaasChatbotConfigValue('namespace') ?: 'default'; 360 } 361 362 /** 363 * Get chatbot store ID. 364 * 365 * @return string 366 */ 367 public function getChatbotStoreId() 368 { 369 return $this->getStoreId(); 370 } 371 372 /** 373 * Get "Powered by" text for chatbot footer. 374 * 375 * @return string 376 */ 377 public function getChatbotPoweredByText() 378 { 379 return $this->getSaasChatbotConfigValue('powered_by_text') ?: 'Powered by Visualwebs AI'; 380 } 381 382 /** 383 * Get "Powered by" link for chatbot footer. 384 * 385 * @return string 386 */ 387 public function getChatbotPoweredByLink() 388 { 389 return $this->getSaasChatbotConfigValue('powered_by_link') ?: 'https://visualwebs.eu'; 390 } 391 392 /** 393 * Get chatbot assets base URL (where n8n-chat-widget.js is hosted). 394 * 395 * @return string 396 */ 397 public function getChatbotAssetsBaseUrl() 398 { 399 return self::SAAS_CHATBOT_ASSETS_BASE_URL; 400 } 401 227 402 } -
visualwebs-ml/trunk/vendor/visualwebs-ml/Helper/ChatbotApiCall.php
r3322994 r3476789 10 10 use Visualwebs\ML\Helper\Visualwebs as ApiHelper; 11 11 use Visualwebs\ML\Helper\Chatbot as ChatbotHelper; 12 use Visualwebs\ML\Helper\OpenAi as OpenAiHelper; 13 use Visualwebs\ML\Helper\Pinecone as PineconeHelper; 12 use Visualwebs\ML\Helper\Data as DataHelper; 14 13 15 14 class ChatbotApiCall … … 26 25 27 26 /** 28 * @var \Visualwebs\ML\Helper\OpenAiHelper 29 */ 30 protected $openAiHelper; 31 32 /** 33 * @var \Visualwebs\ML\Helper\PineconeHelper 34 */ 35 protected $pineconeHelper; 27 * @var \Visualwebs\ML\Helper\DataHelper 28 */ 29 protected $dataHelper; 36 30 37 31 /** … … 49 43 $this->apiHelper = new ApiHelper(); 50 44 $this->chatbotHelper = new ChatbotHelper(); 51 $this->openAiHelper = new OpenAiHelper(); 52 $this->pineconeHelper = new PineconeHelper(); 45 $this->dataHelper = new DataHelper(); 53 46 $this->debugEnabled = $this->chatbotHelper->getIsDebugChatbotEnabled(); 54 47 } … … 62 55 * @return void 63 56 */ 64 public function execute() 65 { 66 $frontendBaseUrl = $this->chatbotHelper->getFrontendBaseUrl(); 67 $context = $this->chatbotHelper->getChatbotContextValue(); 68 $input = json_decode(file_get_contents('php://input'), true); 57 public function execute($input = null) 58 { 59 $input = is_array($input) ? $input : json_decode(file_get_contents('php://input'), true); 60 if (!is_array($input)) { 61 $input = array(); 62 } 69 63 70 64 $message = isset($input['message']) ? sanitize_text_field(wp_unslash($input['message'])) : ''; … … 74 68 : []; 75 69 76 // Important, we retrieve here because in frontend is cached 77 $uniqueSessionId = $this->chatbotHelper->getConversationUniqueSessionId(); 78 $openAiApiKey = $this->openAiHelper->getApiKey(); 79 $offMessage = $this->chatbotHelper->getChatbotOfflineMessage(); 80 $useCustomerApiKeysEnabled = $this->chatbotHelper->getUseCustomerApiKeysEnabled(); 81 $domainNamespace = $this->chatbotHelper->getDomainNamespace(); 82 83 if ($useCustomerApiKeysEnabled) { 84 $args = [ 85 'openai_api_key' => $openAiApiKey, 86 'session_id' => $uniqueSessionId, 87 'is_single_prompt' => !$isChat 88 ]; 89 } else { 90 $args = [ 91 'session_id' => $uniqueSessionId, 92 'is_single_prompt' => !$isChat 93 ]; 94 } 95 96 if ($isChat) { 97 98 $args['instructions'] = $context; 99 $args['catalog_search_url'] = $frontendBaseUrl . "?s="; 100 $args['tools'] = ["get_order_status", "get_product_stock"]; 101 $args['tools_endpoint'] = $this->chatbotHelper->getCallbackApiUrl(); 102 $args['current_page_info'] = $currentPageInfo; 103 if ($offMessage && trim($offMessage)) { 104 $args['offline_message'] = $offMessage; 105 } 106 107 if ($this->chatbotHelper->getIsSemanticSearchEnabled()) { 108 109 // We offer Pinecone only at this moment 110 111 $pineconeApiKey = $this->pineconeHelper->getPineconeApiKey(); 112 $pineconeHost = $this->pineconeHelper->getPineconeIndexHost(); 113 $pineconeNamespace = $this->pineconeHelper->getPineconeNamespace(); 114 $vectorSearchMinScore = $this->chatbotHelper->getVectorSearchMinScore(); 115 116 if ($useCustomerApiKeysEnabled) { 117 $args = array_merge($args, [ 118 'pinecone_api_key' => $pineconeApiKey, 119 'pinecone_index_host' => $pineconeHost, 120 'pinecone_namespace' => $pineconeNamespace, 121 'vector_search_min_score' => $vectorSearchMinScore 122 ]); 123 } else { 124 $args = array_merge($args, [ 125 'pinecone_namespace' => $domainNamespace, 126 'vector_search_min_score' => $vectorSearchMinScore 127 ]); 128 } 129 } 130 } 131 132 $this->setArgs($args); 133 134 try { 135 $success = true; 136 if ($isChat) { 137 $reply = $this->apiHelper->sendChatRequest($message, $this->getArgs()); 138 } else { 139 $reply = $this->apiHelper->sendPromptRequest($message, $this->getArgs()); 140 } 141 } catch (\Exception $e) { 142 $reply = __('Sorry, a problem happened, please try again.', 'visualwebs-ml'); 143 $success = false; 144 } 145 146 wp_send_json([ 147 'result' => $reply, 148 'success' => $success 149 ]); 70 if (empty($message)) { 71 return array( 72 'result' => '', 73 'success' => false, 74 'error' => __('Message is required.', 'visualwebs-ml'), 75 ); 76 } 77 78 $payload = array( 79 'message' => $message, 80 'user_message' => $message, 81 'platform' => 'wordpress-frontend', 82 'store_id' => $this->dataHelper->getStoreId(), 83 'params' => array( 84 'session_id' => $this->chatbotHelper->getConversationUniqueSessionId(), 85 'is_single_prompt' => !$isChat, 86 'current_page_info' => $currentPageInfo, 87 'instructions' => $this->chatbotHelper->getChatbotContextValue(), 88 'offline_message' => $this->chatbotHelper->getChatbotOfflineMessage(), 89 ), 90 ); 91 92 error_log('[Visualwebs ML][Chatbot][request] ' . wp_json_encode(array( 93 'endpoint' => $this->chatbotHelper->getChatApiUrl(), 94 'payload_keys' => array_keys($payload), 95 'scope' => 'frontend', 96 ))); 97 98 $response = $this->apiHelper->sendWebhookRequest( 99 $this->chatbotHelper->getChatApiUrl(), 100 $payload, 101 array('x-api-key' => $this->dataHelper->getApiKey()) 102 ); 103 104 error_log('[Visualwebs ML][Chatbot][response] ' . wp_json_encode(array( 105 'scope' => 'frontend', 106 'success' => empty($response['error']), 107 'has_result' => isset($response['result']) || isset($response['reply']) || isset($response['message']), 108 ))); 109 110 return array( 111 'result' => isset($response['result']) 112 ? $response['result'] 113 : (isset($response['reply']) ? $response['reply'] : (isset($response['message']) ? $response['message'] : '')), 114 'success' => empty($response['error']), 115 'error' => isset($response['error']) ? $response['error'] : '', 116 ); 117 } 118 119 /** 120 * Execute admin chatbot call via secure local proxy endpoint. 121 * 122 * @param array|null $input 123 * @return array 124 */ 125 public function execute_admin($input = null) 126 { 127 $input = is_array($input) ? $input : json_decode(file_get_contents('php://input'), true); 128 if (!is_array($input)) { 129 $input = array(); 130 } 131 132 $message = isset($input['message']) ? sanitize_text_field(wp_unslash($input['message'])) : ''; 133 if (empty($message)) { 134 return array( 135 'result' => '', 136 'success' => false, 137 'error' => __('Message is required.', 'visualwebs-ml'), 138 ); 139 } 140 141 $payload = array( 142 'message' => $message, 143 'user_message' => $message, 144 'platform' => 'wordpress-admin', 145 'store_id' => $this->dataHelper->getStoreId(), 146 'params' => array( 147 'session_id' => 'admin_' . get_current_user_id() . '_' . wp_generate_uuid4(), 148 'is_single_prompt' => false, 149 'is_admin' => true, 150 'current_page_info' => array( 151 'full_action_name' => DataHelper::getFullActionName(), 152 'params' => array(), 153 ), 154 ), 155 ); 156 157 error_log('[Visualwebs ML][Chatbot][request] ' . wp_json_encode(array( 158 'endpoint' => $this->chatbotHelper->getChatApiUrl(), 159 'payload_keys' => array_keys($payload), 160 'scope' => 'admin', 161 'user_id' => get_current_user_id(), 162 ))); 163 164 $response = $this->apiHelper->sendWebhookRequest( 165 $this->chatbotHelper->getChatApiUrl(), 166 $payload, 167 array('x-api-key' => $this->dataHelper->getApiKey()) 168 ); 169 170 error_log('[Visualwebs ML][Chatbot][response] ' . wp_json_encode(array( 171 'scope' => 'admin', 172 'success' => empty($response['error']), 173 'has_result' => isset($response['result']) || isset($response['reply']) || isset($response['message']), 174 ))); 175 176 return array( 177 'result' => isset($response['result']) 178 ? $response['result'] 179 : (isset($response['reply']) ? $response['reply'] : (isset($response['message']) ? $response['message'] : '')), 180 'success' => empty($response['error']), 181 'error' => isset($response['error']) ? $response['error'] : '', 182 ); 150 183 } 151 184 -
visualwebs-ml/trunk/vendor/visualwebs-ml/Helper/Data.php
r3339941 r3476789 33 33 34 34 /** 35 * Retrieve Visualwebs API key. 36 * 37 * @return string 38 */ 39 public function getApiKey() 40 { 41 return (string) $this->getConfig('api_key', ''); 42 } 43 44 /** 45 * Retrieve SaaS store UUID. 46 * 47 * @return string 48 */ 49 public function getStoreId() 50 { 51 return (string) $this->getConfig('store_id', ''); 52 } 53 54 /** 35 55 * Check if the Visualwebs ML module is enabled. 36 56 * … … 78 98 return $isMlServicesEnabled && $isMainModuleEnabled && $mlServicesApiKey; 79 99 } 80 81 /**82 * Check if the use of customer API keys is enabled.83 *84 * This method retrieves the configuration setting that determines whether85 * the use of customer OpenAI/PInecone API keys is enabled for the Visualwebs ML services.86 *87 * @return bool Returns true if the use of customer API keys is enabled, false otherwise.88 */89 public function getUseCustomerApiKeysEnabled()90 {91 $isUseCustomerApiKeysEnabled = $this->getConfig('use_customer_api_keys');92 93 return $isUseCustomerApiKeysEnabled;94 }95 96 100 /** 97 101 * Check if Dynamic Pricing is enabled. … … 123 127 124 128 /** 125 * Get the minimum profit percentage for Dynamic Pricing.126 *127 * @return float128 */129 public function getDynamicPricingMinProfit()130 {131 return (float) $this->getConfig('dynamic_pricing_min_profit', 10);132 }133 134 /**135 * Get the maximum profit percentage for Dynamic Pricing.136 *137 * @return float138 */139 public function getDynamicPricingMaxProfit()140 {141 return (float) $this->getConfig('dynamic_pricing_max_profit', 35);142 }143 144 /**145 129 * Check if dynamic pricing can replace prices (enabled AND replace prices). 146 130 * … … 150 134 { 151 135 return $this->getIsDynamicPricingEnabled() && $this->getIsDynamicPricingReplacePrices(); 152 }153 154 /**155 * Check if storing dynamic data (like product price and stock) is enabled.156 *157 * @return bool158 */159 public function getIsStoreDynamicDataEnabled()160 {161 return (bool) $this->getConfig('vector_store_dynamic_data', false);162 136 } 163 137 … … 197 171 $currencySymbol = get_woocommerce_currency_symbol(); 198 172 return $currencySymbol ?: "€"; 199 }200 201 /**202 * Retrieve user widgets configuration.203 *204 * This method fetches the user widgets configuration value from the store's scope configuration.205 *206 * @return string JSON encoded string of user widgets configuration.207 * Returns an empty JSON array "[]" if no configuration is found.208 */209 210 public function getUserWidgets()211 {212 $userWidgets = $this->getConfig('user_widgets');213 $processedWidgets = [];214 215 if (!empty($userWidgets['redux_repeater_data'])) {216 foreach ($userWidgets['redux_repeater_data'] as $index => $widgetData) {217 $processedWidgets[] = [218 'widget_title' => $userWidgets['widget_title'][$index] ?? '',219 'widget_instructions' => $userWidgets['widget_instructions'][$index] ?? '',220 'widget_render_el' => $userWidgets['widget_render_el'][$index] ?? '',221 'widget_source_el' => $userWidgets['widget_source_el'][$index] ?? '',222 ];223 }224 }225 226 return $processedWidgets;227 173 } 228 174 … … 322 268 { 323 269 $content = ''; 324 $isStoreDynamicDataEnabled = $this->getIsStoreDynamicDataEnabled(); 270 // Store dynamic data config was removed (now managed in SaaS feed generation) 271 $isStoreDynamicDataEnabled = false; 325 272 326 273 if (post_type_exists($postType)) { -
visualwebs-ml/trunk/vendor/visualwebs-ml/Helper/Enqueue.php
r3337202 r3476789 3 3 namespace Visualwebs\ML\Helper; 4 4 5 use Visualwebs\ML\Block\ChatWidget;6 use Visualwebs\ML\Block\Adminhtml\Widget;7 use Visualwebs\ML\Block\Adminhtml\Dashboard;8 use Visualwebs\ML\Helper\Data as Helper;9 use Visualwebs\ML\Model\Config\Source\MLModelType;10 11 5 class Enqueue 12 6 { 13 private $widget;14 private $dashboard;15 private $chatWidget;16 17 7 /** 18 * Constructor to initialize dependencies.8 * Constructor. 19 9 */ 20 10 public function __construct() 21 11 { 22 $this->widget = new Widget(); 23 $this->dashboard = new Dashboard(); 24 $this->chatWidget = new ChatWidget(); 12 // Lightweight enqueue class for 5.5.0+ 25 13 } 26 14 … … 32 20 public function enqueue_admin_scripts($hook) 33 21 { 34 wp_enqueue_style('visualwebs-ml-admin-style', plugin_dir_url(__FILE__) . '../../../assets/css/admin-style.css', [], false); 35 36 if ($this->widget->getIsWidgetEnabled()) { 37 38 $nonce = $this->widget->getAppNonce(); 39 $cache_key = Helper::CACHE_TRANSIENT; 40 $cached_data = get_transient($cache_key); 41 42 if ($cached_data === false) { 43 $cached_data = [ 44 'salesLiveDataset' => $this->widget->getStoreSalesData(), 45 'bestsellerLiveDataset' => $this->widget->getProductBestSellersData(), 46 'bestsellerMeta' => $this->widget->getProductBestSellersMetadata(), 47 'reviewsLiveDataset' => $this->widget->getProductsReviewsData(), 48 'customerRegistrationLiveDataset' => $this->widget->getCustomerRegistrationData(), 49 'MLApiUrl' => $this->widget->getMLApiUrl(), 50 'chatApiUrl' => $this->widget->getChatApiUrl(), 51 'timeseriesModel' => MLModelType::TYPE_TEMPORAL_SERIES_DEFAULT, 52 'sentimentModel' => MLModelType::TYPE_TEXT_SENTIMENT, 53 'spamModel' => MLModelType::TYPE_SPAM_CLASSIFIER, 54 'userWidgets' => $this->widget->getUserWidgets() ?: [] 55 ]; 56 57 set_transient($cache_key, $cached_data, HOUR_IN_SECONDS); 58 } 59 60 $dynamic_data = [ 61 'productId' => $this->widget->getProduct() ? $this->widget->getProduct()->get_id() : '0', 62 'productRef' => $this->widget->getProduct() ? $this->widget->getProduct()->get_sku() : 'NA', 63 'productLiveDataset' => $this->widget->getProduct() ? $this->widget->getProductData() : [], 64 'userLanguage' => $this->widget->getCurrentAdminUserLanguageShort(), 65 'nonce' => $nonce 66 ]; 67 68 $translated_titles = [ 69 'salesPredictorTitle' => esc_html__('Sales Predictor', 'visualwebs-ml'), 70 'reviewsSentimentTitle' => esc_html__('Reviews Sentiment', 'visualwebs-ml'), 71 'bestsellersPredictorTitle' => esc_html__('Bestsellers Predictor', 'visualwebs-ml'), 72 'reviewsSpamTitle' => esc_html__('Reviews Spam', 'visualwebs-ml'), 73 'apiUsageTitle' => esc_html__('API usage', 'visualwebs-ml'), 74 'chatbotHistoryTitle' => esc_html__('Chatbot history', 'visualwebs-ml'), 75 'customerRegisterPredictorTitle' => esc_html__('Customer Register Predictor', 'visualwebs-ml'), 76 'productPredictorTitle' => esc_html__('Product Predictor', 'visualwebs-ml'), 77 'promptTitle' => esc_html__('Prompt', 'visualwebs-ml'), 78 'seoGeneratorTitle' => esc_html__('SEO generator', 'visualwebs-ml'), 79 'seoGeneratorDescriptionTitle' => esc_html__('SEO generator - Description', 'visualwebs-ml'), 80 'seoGeneratorShortDescriptionTitle' => esc_html__('SEO generator - Short description', 'visualwebs-ml') 81 ]; 82 83 $final_data = array_merge($cached_data, $dynamic_data, $translated_titles); 84 85 wp_enqueue_script( 86 'visualwebs-ml-app-remote-js', 87 'https://ml.visualwebs.eu/app.js', 88 array(), 89 false, 90 ['in_footer' => true, 'strategy' => 'defer'] 91 ); 92 93 wp_enqueue_script( 94 'visualwebs-widget-js', 95 plugin_dir_url(__FILE__) . '../../../assets/js/widget.js', 96 array(), 97 false, 98 ['in_footer' => true, 'strategy' => 'defer'] 99 ); 100 101 wp_localize_script('visualwebs-widget-js', 'visualwebsAiWidgetData', $final_data); 102 103 } 104 105 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required for this specific use case. 106 if (isset($_GET['page']) && $_GET['page'] === 'visualwebs-ml-dashboard') { 107 108 if ($this->dashboard->getIsVisualwebsMlServicesEnabled()) { 109 110 $stats = $this->dashboard->getStats(); 111 $userLanguage = $this->dashboard->getCurrentAdminUserLanguageShort(); 112 $currency_symbol = html_entity_decode($this->dashboard->getStoreCurrencySign()); 113 $nonce = $this->dashboard->getAppNonce(); 114 $clear_cache_url = esc_url(admin_url('admin.php?page=visualwebs-ml-dashboard&clear_cache=true&_wpnonce=' . wp_create_nonce('clear_cache_action'))); 115 116 wp_enqueue_script( 117 'visualwebs-dashboard-js', 118 plugin_dir_url(__FILE__) . '../../../assets/js/dashboard.js', 119 array(), 120 false, 121 ['in_footer' => true, 'strategy' => 'defer'] 122 ); 123 124 wp_localize_script('visualwebs-dashboard-js', 'visualwebsAiDashboardData', [ 125 'stats' => $stats, 126 'currencySymbol' => $currency_symbol, 127 'clearCacheUrl' => $clear_cache_url, 128 'userLanguage' => $userLanguage, 129 'nonce' => $nonce 130 ]); 131 132 } 133 } 134 135 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required for this specific use case. 136 if (isset($_GET['page']) && $_GET['page'] === 'visualwebs-ml-semantic-add') { 137 wp_enqueue_script( 138 'visualwebs-ml-semantic-search', 139 plugin_dir_url(__FILE__) . '../../../assets/js/semantic-search.js', 140 array('jquery'), 141 false, 142 ['in_footer' => true, 'strategy' => 'defer'] 143 ); 144 145 wp_localize_script( 146 'visualwebs-ml-semantic-search', 147 'visualwebs_ml_search', 148 array( 149 'ajax_url' => admin_url('admin-ajax.php') 150 ) 151 ); 152 153 } 154 22 // Legacy widgets and dashboard removed in 5.5.0 (now managed in SaaS dashboard) 23 // Legacy chatbot removed in 5.5.0 (now uses n8n-chatbot-widget.php template) 24 // Legacy semantic search admin removed in 5.5.0 25 // Legacy admin styles removed in 5.5.0 155 26 } 156 27 … … 160 31 public function enqueue_frontend_scripts() 161 32 { 162 if ($this->chatWidget->getIsChatbotEnabled()) { 163 $chatApiUrl = $this->chatWidget->getChatApiUrl(); 164 $chatbotCurrentPageInfo = $this->chatWidget->getChatbotCurrentPageInfoForWidget(); 165 $linksColor = $this->chatWidget->getChatbotMainBackgroundColor(); 166 $mainBackgroundColor = $this->chatWidget->getChatbotMainBackgroundColor(); 167 $chatbotHeaderTitle = $this->chatWidget->getChatbotHeaderTitle(); 168 $chatbotDisclaimerUrl = $this->chatWidget->getChatbotDisclaimerUrl(); 169 $chatbotOfflineMessage = $this->chatWidget->getChatbotOfflineMessage(); 170 $chatbotLogoUrl = $this->chatWidget->getChatbotLogoUrl(); 171 $userLanguage = $this->chatWidget->getCurrentFrontendLanguageShort(); 172 173 wp_enqueue_script( 174 'visualwebs-ml-app-remote-js', 175 'https://ml.visualwebs.eu/app.js', 176 array(), 177 false, 178 ['in_footer' => true, 'strategy' => 'defer'] 179 ); 180 181 wp_enqueue_script('visualwebs-chatbot-js', plugin_dir_url(__FILE__) . '../../../assets/js/chatbot.js', ['visualwebs-ml-app-remote-js'], false, ['in_footer' => true, 'strategy' => 'defer']); 182 183 $inline_data = 'var visualwebsAiChatbotData = ' . wp_json_encode([ 184 "chatApiUrl" => $chatApiUrl, 185 "uniqueSessionId" => '', 186 "chatbotCurrentPageInfo" => $chatbotCurrentPageInfo, 187 "mainBackgroundColor" => $mainBackgroundColor, 188 "linksColor" => $linksColor, 189 "chatbotHeaderTitle" => $chatbotHeaderTitle, 190 "chatbotDisclaimerUrl" => $chatbotDisclaimerUrl, 191 "chatbotOfflineMessage" => $chatbotOfflineMessage, 192 "chatbotLogoUrl" => $chatbotLogoUrl, 193 "userLanguage" => $userLanguage, 194 'restUrl' => esc_url_raw(rest_url('visualwebs-ml/v1/get-nonce')) 195 ]) . ';'; 196 197 wp_add_inline_script('visualwebs-chatbot-js', $inline_data, 'before'); 198 } 33 // Legacy chatbot enqueue removed in 5.5.0 34 // Now uses n8n-chatbot-widget.php template loaded via wp_footer hook 35 // See: class-visualwebs-ml.php render_frontend_chatbot_widget() 199 36 } 200 37 } -
visualwebs-ml/trunk/vendor/visualwebs-ml/Helper/Visualwebs.php
r3337157 r3476789 111 111 112 112 /** 113 * Sends payload to an explicit webhook URL. 114 * 115 * @param string $url 116 * @param array $data 117 * @param array $headers 118 * @return array 119 */ 120 public function sendWebhookRequest($url, $data = [], $headers = []) 121 { 122 $visualwebsApi = $this->getVisualwebsInstance(); 123 return $visualwebsApi->sendWebhookRequest($url, $data, $headers); 124 } 125 126 /** 113 127 * Sends a vector upsert request to the Visualwebs API. 114 128 * -
visualwebs-ml/trunk/vendor/visualwebs-ml/Model/Api/Visualwebs.php
r3320111 r3476789 97 97 98 98 /** 99 * Sends a request to an explicit webhook URL (used by SaaS chatbot n8n endpoint). 100 * 101 * @param string $url 102 * @param array $data 103 * @param array $extraHeaders 104 * @return array 105 */ 106 public function sendWebhookRequest($url, $data = [], $extraHeaders = []) 107 { 108 if (empty($url)) { 109 return [ 110 'error' => true, 111 'message' => 'Missing webhook URL', 112 ]; 113 } 114 115 $headers = [ 116 'Content-Type' => 'application/json', 117 'Accept' => 'application/json', 118 ]; 119 120 // Keep compatibility with existing API key auth model. 121 if (!empty($this->apiKey)) { 122 $headers['Authorization'] = 'Bearer ' . $this->apiKey; 123 $headers['x-api-key'] = $this->apiKey; 124 } 125 126 if (!empty($extraHeaders) && is_array($extraHeaders)) { 127 $headers = array_merge($headers, $extraHeaders); 128 } 129 130 $response = wp_remote_post( 131 $url, 132 [ 133 'headers' => $headers, 134 'timeout' => 20, 135 'body' => wp_json_encode($data), 136 ] 137 ); 138 139 if (is_wp_error($response)) { 140 return [ 141 'error' => true, 142 'message' => $response->get_error_message(), 143 ]; 144 } 145 146 $status = (int) wp_remote_retrieve_response_code($response); 147 $body = wp_remote_retrieve_body($response); 148 $decoded = json_decode($body, true); 149 150 if ($status < 200 || $status >= 300) { 151 return [ 152 'error' => true, 153 'message' => 'Webhook response status: ' . $status, 154 'raw' => is_array($decoded) ? $decoded : $body, 155 ]; 156 } 157 158 return is_array($decoded) ? $decoded : ['result' => (string) $body]; 159 } 160 161 /** 99 162 * Sends a spam prediction request to the specified endpoint. 100 163 * -
visualwebs-ml/trunk/vendor/visualwebs-ml/Model/MLModel.php
r3326860 r3476789 658 658 659 659 $tomorrow = gmdate('Y-m-d', strtotime('+1 day')); 660 $min_pct = $this->mainHelper->getDynamicPricingMinProfit(); 661 $max_pct = $this->mainHelper->getDynamicPricingMaxProfit(); 660 // Default profit margins (config removed, now managed in SaaS) 661 $min_pct = 10; 662 $max_pct = 35; 662 663 $result = []; 663 664 -
visualwebs-ml/trunk/visualwebs-ml.php
r3337202 r3476789 4 4 Requires Plugins: redux-framework, woocommerce 5 5 Description: Plugin to embed chatGPT, chatbot, and Machine Learning Widgets in WP. 6 Version: 5. 4.36 Version: 5.5.0 7 7 Requires PHP: 7.4 8 8 Author: Visualwebs 9 9 Author URI: https://visualwebs.eu/product/ai-cloud-suite/ 10 Tested up to: 6. 810 Tested up to: 6.9 11 11 License: GPLv2 or later 12 12 License URI: https://visualwebs.eu/terms-and-conditions/
Note: See TracChangeset
for help on using the changeset viewer.