| <?php |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| if (!defined('ABSPATH')) { |
| exit; |
| } |
|
|
| |
| define('PHI_SHAMPOO_VERSION', '1.0'); |
| define('PHI_SHAMPOO_PLUGIN_DIR', plugin_dir_path(__FILE__)); |
| define('PHI_SHAMPOO_PLUGIN_URL', plugin_dir_url(__FILE__)); |
| define('PHI_SHAMPOO_PLUGIN_BASENAME', plugin_basename(__FILE__)); |
|
|
| |
| define('PHI_SHAMPOO_TEXT_MODEL', 'microsoft/Phi-3.5-mini-instruct'); |
| define('PHI_SHAMPOO_VISION_MODEL', 'microsoft/Phi-3.5-vision-instruct'); |
| define('PHI_SHAMPOO_HF_TEXT_API_URL', 'https://api-inference.huggingface.co/models/' . PHI_SHAMPOO_TEXT_MODEL); |
| define('PHI_SHAMPOO_HF_VISION_API_URL', 'https://api-inference.huggingface.co/models/' . PHI_SHAMPOO_VISION_MODEL); |
|
|
| |
| |
| |
| function phi_shampoo_activate() { |
| |
| phi_shampoo_create_pages(); |
| |
| |
| if (!get_option('phi_shampoo_test_mode')) { |
| update_option('phi_shampoo_test_mode', true); |
| } |
| |
| |
| flush_rewrite_rules(); |
| } |
| register_activation_hook(__FILE__, 'phi_shampoo_activate'); |
|
|
| |
| |
| |
| function phi_shampoo_deactivate() { |
| |
| flush_rewrite_rules(); |
| } |
| register_deactivation_hook(__FILE__, 'phi_shampoo_deactivate'); |
|
|
| |
| |
| |
| function phi_shampoo_create_pages() { |
| |
| $analyzer_page_id = wp_insert_post(array( |
| 'post_title' => 'Phi Shampoo Analyzer', |
| 'post_content' => '[phi_shampoo_analyzer]', |
| 'post_status' => 'publish', |
| 'post_type' => 'page', |
| 'comment_status' => 'closed' |
| )); |
| |
| |
| update_option('phi_shampoo_analyzer_page_id', $analyzer_page_id); |
| } |
|
|
| |
| |
| |
| function phi_shampoo_enqueue_scripts() { |
| |
| wp_register_style('phi-shampoo-style', PHI_SHAMPOO_PLUGIN_URL . 'assets/css/style.css', array(), PHI_SHAMPOO_VERSION); |
| wp_enqueue_style('phi-shampoo-style'); |
| |
| |
| wp_register_script('phi-shampoo-script', PHI_SHAMPOO_PLUGIN_URL . 'assets/js/phi-shampoo.js', array('jquery'), PHI_SHAMPOO_VERSION, true); |
| wp_enqueue_script('phi-shampoo-script'); |
| |
| |
| wp_localize_script('phi-shampoo-script', 'phiShampoo', array( |
| 'ajax_url' => admin_url('admin-ajax.php'), |
| 'nonce' => wp_create_nonce('phi_shampoo_nonce'), |
| 'analyzePage' => get_permalink(get_option('phi_shampoo_analyzer_page_id')) |
| )); |
| } |
| add_action('wp_enqueue_scripts', 'phi_shampoo_enqueue_scripts'); |
|
|
| |
| |
| |
| function phi_shampoo_log($message) { |
| if (defined('WP_DEBUG') && WP_DEBUG) { |
| $log_file = WP_CONTENT_DIR . '/debug.log'; |
| $timestamp = current_time('mysql'); |
| if (is_array($message) || is_object($message)) { |
| error_log('[' . $timestamp . '] Phi Shampoo: ' . print_r($message, true) . "\n", 3, $log_file); |
| } else { |
| error_log('[' . $timestamp . '] Phi Shampoo: ' . $message . "\n", 3, $log_file); |
| } |
| } |
| } |
|
|
| |
| |
| |
| function phi_shampoo_add_settings_link($links) { |
| $settings_link = '<a href="' . admin_url('admin.php?page=phi-shampoo-settings') . '">' . __('Settings', 'phi-shampoo-analyzer') . '</a>'; |
| array_unshift($links, $settings_link); |
| return $links; |
| } |
| add_filter('plugin_action_links_' . PHI_SHAMPOO_PLUGIN_BASENAME, 'phi_shampoo_add_settings_link'); |
|
|
| |
| |
| |
| function phi_shampoo_register_shortcodes() { |
| add_shortcode('phi_shampoo_analyzer', 'phi_shampoo_analyzer_shortcode'); |
| } |
| add_action('init', 'phi_shampoo_register_shortcodes'); |
|
|
| |
| |
| |
| function phi_shampoo_analyzer_shortcode() { |
| ob_start(); |
| ?> |
| <div class="phi-shampoo-analyzer"> |
| <h2>Phi Shampoo Analyzer</h2> |
| <p>Enter shampoo ingredients below or upload an image of the ingredient list.</p> |
| |
| <form id="phi-analyzer-form" method="post" enctype="multipart/form-data"> |
| <?php wp_nonce_field('phi_shampoo_analyze', 'phi_shampoo_nonce'); ?> |
| |
| <div class="phi-form-group"> |
| <label for="phi-ingredients">Ingredients:</label> |
| <textarea id="phi-ingredients" name="ingredients" rows="5" placeholder="Enter ingredients separated by commas..."></textarea> |
| </div> |
| |
| <div class="phi-form-group"> |
| <label for="phi-image">Or upload image:</label> |
| <input type="file" id="phi-image" name="ingredient_image" accept="image/*"> |
| </div> |
| |
| <div class="phi-form-group"> |
| <label for="phi-allergies">Known Allergies (optional):</label> |
| <input type="text" id="phi-allergies" name="allergies" placeholder="e.g., Parabens, Sulfates, Fragrances"> |
| </div> |
| |
| <div class="phi-form-group"> |
| <label for="phi-skin-type">Skin Type (optional):</label> |
| <select id="phi-skin-type" name="skin_type"> |
| <option value="">Select Skin Type</option> |
| <option value="Normal">Normal</option> |
| <option value="Dry">Dry</option> |
| <option value="Oily">Oily</option> |
| <option value="Combination">Combination</option> |
| <option value="Sensitive">Sensitive</option> |
| </select> |
| </div> |
| |
| <div class="phi-form-submit"> |
| <button type="submit" class="phi-submit-button">Analyze</button> |
| </div> |
| </form> |
| |
| <div id="phi-result" class="phi-result" style="display: none;"> |
| <h3>Analysis Result</h3> |
| <div id="phi-result-content"></div> |
| </div> |
| |
| <div id="phi-loading" class="phi-loading" style="display: none;"> |
| <p>Analyzing ingredients... Please wait.</p> |
| </div> |
| </div> |
| |
| <script> |
| jQuery(document).ready(function($) { |
| $('#phi-analyzer-form').on('submit', function(e) { |
| e.preventDefault(); |
| |
| $('#phi-loading').show(); |
| $('#phi-result').hide(); |
| |
| var formData = new FormData(this); |
| formData.append('action', 'phi_analyze_shampoo'); |
| |
| $.ajax({ |
| url: phiShampoo.ajax_url, |
| type: 'POST', |
| data: formData, |
| contentType: false, |
| processData: false, |
| success: function(response) { |
| $('#phi-loading').hide(); |
| |
| if (response.success) { |
| $('#phi-result-content').html(response.data.result); |
| $('#phi-result').show(); |
| } else { |
| $('#phi-result-content').html('<p class="phi-error">Error: ' + response.data.message + '</p>'); |
| $('#phi-result').show(); |
| } |
| }, |
| error: function() { |
| $('#phi-loading').hide(); |
| $('#phi-result-content').html('<p class="phi-error">Error: Could not connect to the server.</p>'); |
| $('#phi-result').show(); |
| } |
| }); |
| }); |
| }); |
| </script> |
| <?php |
| return ob_get_clean(); |
| } |
|
|
| |
| |
| |
| function phi_shampoo_ajax_analyze() { |
| |
| if (!isset($_POST['phi_shampoo_nonce']) || !wp_verify_nonce($_POST['phi_shampoo_nonce'], 'phi_shampoo_analyze')) { |
| wp_send_json_error(array('message' => 'Security check failed.')); |
| } |
| |
| $ingredients = ''; |
| $image_uploaded = false; |
| |
| |
| if (!empty($_FILES['ingredient_image']['tmp_name'])) { |
| $image_uploaded = true; |
| $tmp_name = $_FILES['ingredient_image']['tmp_name']; |
| |
| |
| $result = phi_shampoo_process_image($tmp_name); |
| |
| if ($result['success']) { |
| $ingredients = $result['ingredients']; |
| } else { |
| wp_send_json_error(array('message' => 'Could not extract ingredients from image: ' . $result['error'])); |
| } |
| } else if (!empty($_POST['ingredients'])) { |
| $ingredients = sanitize_textarea_field($_POST['ingredients']); |
| } else { |
| wp_send_json_error(array('message' => 'Please provide ingredients or upload an image.')); |
| } |
| |
| |
| $allergies = isset($_POST['allergies']) ? sanitize_text_field($_POST['allergies']) : ''; |
| $skin_type = isset($_POST['skin_type']) ? sanitize_text_field($_POST['skin_type']) : ''; |
| |
| |
| $analysis_data = array( |
| 'ingredients' => $ingredients, |
| 'allergies' => $allergies, |
| 'skin_type' => $skin_type |
| ); |
| |
| $analysis = phi_shampoo_analyze_ingredients($analysis_data); |
| |
| if ($analysis['success']) { |
| $html_result = '<div class="phi-analysis">'; |
| |
| if ($image_uploaded) { |
| $html_result .= '<p><strong>Extracted Ingredients:</strong> ' . esc_html($ingredients) . '</p>'; |
| } |
| |
| $html_result .= '<div class="phi-analysis-content">' . nl2br(esc_html($analysis['result'])) . '</div>'; |
| $html_result .= '</div>'; |
| |
| wp_send_json_success(array('result' => $html_result)); |
| } else { |
| wp_send_json_error(array('message' => 'Analysis failed: ' . $analysis['error'])); |
| } |
| } |
| add_action('wp_ajax_phi_analyze_shampoo', 'phi_shampoo_ajax_analyze'); |
| add_action('wp_ajax_nopriv_phi_analyze_shampoo', 'phi_shampoo_ajax_analyze'); |
|
|
| |
| |
| |
| function phi_shampoo_process_image($image_path) { |
| |
| $test_mode = get_option('phi_shampoo_test_mode', false); |
| if ($test_mode) { |
| phi_shampoo_log("Test mode active - returning test ingredients from image"); |
| return array( |
| 'success' => true, |
| 'ingredients' => "Water, Sodium Laureth Sulfate, Cocamidopropyl Betaine, Sodium Chloride, Glycol Distearate, Dimethiconol, Fragrance, Carbomer, Guar Hydroxypropyltrimonium Chloride, Tetrasodium EDTA, Citric Acid, Sodium Benzoate, Methylchloroisothiazolinone, Methylisothiazolinone" |
| ); |
| } |
| |
| |
| $api_token = phi_shampoo_get_api_token(); |
| |
| if (empty($api_token)) { |
| phi_shampoo_log("Error: Missing API token for Hugging Face"); |
| return array( |
| 'success' => false, |
| 'error' => 'Missing API token. Please configure it in plugin settings.', |
| 'ingredients' => '' |
| ); |
| } |
| |
| |
| if (!file_exists($image_path)) { |
| phi_shampoo_log("Error: Image file not found at path: " . $image_path); |
| return array( |
| 'success' => false, |
| 'error' => 'Image file not found.', |
| 'ingredients' => '' |
| ); |
| } |
| |
| |
| $image_data = file_get_contents($image_path); |
| $base64_image = base64_encode($image_data); |
| |
| |
| $api_url = defined('PHI_SHAMPOO_HF_VISION_API_URL') ? PHI_SHAMPOO_HF_VISION_API_URL : ''; |
| |
| $response = wp_remote_post($api_url, array( |
| 'headers' => array( |
| 'Authorization' => 'Bearer ' . $api_token, |
| 'Content-Type' => 'application/json' |
| ), |
| 'body' => json_encode(array( |
| 'inputs' => array( |
| 'image' => $base64_image, |
| 'text' => "Extract all the ingredients from this shampoo product label. Format them as a comma-separated list." |
| ) |
| )), |
| 'timeout' => 60 |
| )); |
| |
| |
| if (is_wp_error($response)) { |
| phi_shampoo_log("API Error: " . $response->get_error_message()); |
| return array( |
| 'success' => false, |
| 'error' => $response->get_error_message(), |
| 'ingredients' => '' |
| ); |
| } |
| |
| $body = wp_remote_retrieve_body($response); |
| $status_code = wp_remote_retrieve_response_code($response); |
| |
| phi_shampoo_log("API Status: " . $status_code . ", Response: " . substr($body, 0, 200) . "..."); |
| |
| if ($status_code !== 200) { |
| $error_message = "API Error: Received status code " . $status_code; |
| |
| $decoded = json_decode($body, true); |
| if ($decoded && isset($decoded['error'])) { |
| $error_message .= " - " . $decoded['error']; |
| } |
| |
| return array( |
| 'success' => false, |
| 'error' => $error_message, |
| 'ingredients' => '' |
| ); |
| } |
| |
| $decoded = json_decode($body, true); |
| |
| if (isset($decoded['generated_text'])) { |
| return array( |
| 'success' => true, |
| 'ingredients' => $decoded['generated_text'] |
| ); |
| } else { |
| return array( |
| 'success' => false, |
| 'error' => 'Unexpected response format', |
| 'ingredients' => '' |
| ); |
| } |
| } |
|
|
| |
| |
| |
| function phi_shampoo_analyze_ingredients($data) { |
| |
| $test_mode = get_option('phi_shampoo_test_mode', false); |
| if ($test_mode) { |
| return phi_shampoo_generate_test_analysis($data); |
| } |
| |
| |
| $api_token = phi_shampoo_get_api_token(); |
| |
| if (empty($api_token)) { |
| return array( |
| 'success' => false, |
| 'error' => 'Missing API token. Please configure it in plugin settings.', |
| 'result' => 'Error: API token not configured.' |
| ); |
| } |
| |
| |
| if (empty($data['ingredients'])) { |
| return array( |
| 'success' => false, |
| 'error' => 'No ingredients provided for analysis.', |
| 'result' => 'Error: No ingredients to analyze.' |
| ); |
| } |
| |
| |
| $prompt = "You are a shampoo ingredient analyzer. Please analyze the following shampoo ingredients for safety and potential allergic reactions:\n\n"; |
| $prompt .= "Ingredients:\n" . $data['ingredients'] . "\n\n"; |
| |
| |
| if (!empty($data['allergies'])) { |
| $prompt .= "User has allergies to: " . $data['allergies'] . "\n\n"; |
| } |
| |
| if (!empty($data['skin_type'])) { |
| $prompt .= "User has " . $data['skin_type'] . " skin type.\n\n"; |
| } |
| |
| |
| $prompt .= "Please provide a detailed analysis including:\n"; |
| $prompt .= "1. Overall safety assessment (safe or not safe for this user)\n"; |
| $prompt .= "2. Potentially harmful ingredients with explanations\n"; |
| $prompt .= "3. Specific concerns based on the user's health profile\n"; |
| $prompt .= "4. Alternative recommendations if needed\n"; |
| $prompt .= "\nBegin your response with a clear 'SAFE TO USE' or 'NOT RECOMMENDED' statement.\n"; |
| |
| |
| phi_shampoo_log("Analysis prompt: " . $prompt); |
| |
| |
| $api_url = defined('PHI_SHAMPOO_HF_TEXT_API_URL') ? PHI_SHAMPOO_HF_TEXT_API_URL : ''; |
| |
| $response = wp_remote_post($api_url, array( |
| 'headers' => array( |
| 'Authorization' => 'Bearer ' . $api_token, |
| 'Content-Type' => 'application/json' |
| ), |
| 'body' => json_encode(array( |
| 'inputs' => $prompt, |
| 'parameters' => array( |
| 'max_new_tokens' => 512, |
| 'temperature' => 0.7, |
| 'do_sample' => true |
| ) |
| )), |
| 'timeout' => 60 |
| )); |
| |
| |
| if (is_wp_error($response)) { |
| phi_shampoo_log("API Error: " . $response->get_error_message()); |
| return array( |
| 'success' => false, |
| 'error' => $response->get_error_message(), |
| 'result' => 'Error: API request failed. ' . $response->get_error_message() |
| ); |
| } |
| |
| $body = wp_remote_retrieve_body($response); |
| $status_code = wp_remote_retrieve_response_code($response); |
| |
| phi_shampoo_log("API Status: " . $status_code . ", Response: " . substr($body, 0, 200) . "..."); |
| |
| if ($status_code !== 200) { |
| $error_message = "API Error: Received status code " . $status_code; |
| |
| $decoded = json_decode($body, true); |
| if ($decoded && isset($decoded['error'])) { |
| $error_message .= " - " . $decoded['error']; |
| } |
| |
| return array( |
| 'success' => false, |
| 'error' => $error_message, |
| 'result' => 'Error: ' . $error_message |
| ); |
| } |
| |
| $decoded = json_decode($body, true); |
| |
| if (isset($decoded['generated_text'])) { |
| return array( |
| 'success' => true, |
| 'result' => $decoded['generated_text'] |
| ); |
| } else { |
| return array( |
| 'success' => false, |
| 'error' => 'Unexpected response format', |
| 'result' => 'Error: Could not parse the AI response.' |
| ); |
| } |
| } |
|
|
| |
| |
| |
| function phi_shampoo_generate_test_analysis($data) { |
| $ingredients = $data['ingredients']; |
| $has_allergies = !empty($data['allergies']); |
| |
| if (strpos(strtolower($ingredients), 'sulfate') !== false || |
| strpos(strtolower($ingredients), 'methylisothiazolinone') !== false) { |
| |
| $result = "NOT RECOMMENDED\n\n"; |
| $result .= "Based on the ingredients analysis, this shampoo contains some potentially problematic ingredients for your specific health profile.\n\n"; |
| |
| if (strpos(strtolower($ingredients), 'sodium laureth sulfate') !== false || |
| strpos(strtolower($ingredients), 'sodium lauryl sulfate') !== false) { |
| $result .= "• Sodium Laureth/Lauryl Sulfate: A strong detergent that can be irritating, especially for sensitive skin or those with certain allergies.\n"; |
| } |
| |
| if (strpos(strtolower($ingredients), 'methylisothiazolinone') !== false) { |
| $result .= "• Methylisothiazolinone: A preservative that is a known allergen and has been associated with skin sensitization.\n"; |
| } |
| |
| if ($has_allergies) { |
| $result .= "\nBased on your reported allergies, this product contains ingredients that may trigger a reaction. Consider alternative formulations labeled 'sulfate-free' and 'preservative-free'.\n"; |
| } |
| |
| $result .= "\nRecommendation: Look for shampoos with gentler cleansing agents like Cocamidopropyl Hydroxysultaine, Sodium Cocoyl Isethionate or Coco Glucoside."; |
| } else { |
| $result = "SAFE TO USE\n\n"; |
| $result .= "Based on the ingredients analysis, this shampoo appears to be safe for general use. The formula contains:\n\n"; |
| $result .= "• Mild cleansing agents that effectively clean without excessive drying\n"; |
| $result .= "• Conditioning agents to help maintain hair moisture\n"; |
| |
| if (strpos(strtolower($ingredients), 'fragrance') !== false || |
| strpos(strtolower($ingredients), 'parfum') !== false) { |
| $result .= "• Fragrances are present, which could potentially cause sensitivity in some individuals\n"; |
| } |
| |
| $result .= "\nNo major red flags were identified in this formulation that would likely trigger adverse reactions."; |
| } |
| |
| return array( |
| 'success' => true, |
| 'result' => $result |
| ); |
| } |
|
|
| |
| |
| |
| |
| function phi_shampoo_get_api_token() { |
| $token = get_option('phi_shampoo_hf_api_token', ''); |
| |
| |
| return $token; |
| } |
|
|
| |
| |
| |
| function phi_shampoo_admin_menu() { |
| add_menu_page( |
| 'Phi Shampoo Analyzer', |
| 'Phi Shampoo', |
| 'manage_options', |
| 'phi-shampoo-settings', |
| 'phi_shampoo_settings_page', |
| 'dashicons-admin-generic' |
| ); |
| } |
| add_action('admin_menu', 'phi_shampoo_admin_menu'); |
|
|
| |
| |
| |
| function phi_shampoo_settings_page() { |
| |
| if (isset($_POST['phi_shampoo_save_settings']) && isset($_POST['phi_shampoo_settings_nonce'])) { |
| if (wp_verify_nonce($_POST['phi_shampoo_settings_nonce'], 'phi_shampoo_settings')) { |
| |
| |
| if (isset($_POST['phi_shampoo_hf_api_token'])) { |
| $api_token = sanitize_text_field($_POST['phi_shampoo_hf_api_token']); |
| update_option('phi_shampoo_hf_api_token', $api_token); |
| } |
| |
| |
| $test_mode = isset($_POST['phi_shampoo_test_mode']) ? true : false; |
| update_option('phi_shampoo_test_mode', $test_mode); |
| |
| echo '<div class="notice notice-success is-dismissible"><p>Settings saved successfully!</p></div>'; |
| } else { |
| echo '<div class="notice notice-error is-dismissible"><p>Security check failed. Settings not saved.</p></div>'; |
| } |
| } |
| |
| |
| $api_token = get_option('phi_shampoo_hf_api_token', ''); |
| $test_mode = get_option('phi_shampoo_test_mode', true); |
| |
| ?> |
| <div class="wrap"> |
| <h1>Phi Shampoo Analyzer Settings</h1> |
| |
| <form method="post" action=""> |
| <?php wp_nonce_field('phi_shampoo_settings', 'phi_shampoo_settings_nonce'); ?> |
| |
| <table class="form-table"> |
| <tr> |
| <th scope="row">Hugging Face API Token</th> |
| <td> |
| <input type="password" name="phi_shampoo_hf_api_token" value="<?php echo esc_attr($api_token); ?>" class="regular-text" /> |
| <p class="description"> |
| Enter your Hugging Face API token. |
| <a href="https://huggingface.co/settings/tokens" target="_blank">Get a token here</a>. |
| </p> |
| </td> |
| </tr> |
| <tr> |
| <th scope="row">Test Mode</th> |
| <td> |
| <label> |
| <input type="checkbox" name="phi_shampoo_test_mode" <?php checked($test_mode, true); ?> /> |
| Enable test mode (uses pre-defined responses instead of API calls) |
| </label> |
| </td> |
| </tr> |
| </table> |
| |
| <div class="phi-shampoo-settings-info"> |
| <h2>Important Information</h2> |
| <p>To use this plugin, make sure:</p> |
| <ul> |
| <li>You have a valid Hugging Face account</li> |
| <li>You have <a href="https://huggingface.co/settings/inference-providers" target="_blank">enabled the HF Inference API</a> in your Hugging Face account</li> |
| <li>Your API token has the necessary permissions (at least "Read" access)</li> |
| </ul> |
| </div> |
| |
| <p class="submit"> |
| <input type="submit" name="phi_shampoo_save_settings" class="button-primary" value="Save Settings" /> |
| </p> |
| </form> |
| </div> |
| <?php |
| } |