Upload 6 files
Browse files- README.md +66 -0
- assets/.DS_Store +0 -0
- assets/css/style.css +110 -0
- assets/js/phi-shampoo.js +33 -0
- diagnostic.php +195 -0
- phi-shampoo-analyzer.php +658 -0
README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Phi Shampoo Analyzer WordPress Plugin
|
| 2 |
+
|
| 3 |
+
A WordPress plugin that analyzes shampoo ingredients using Microsoft's Phi-3.5 models via the Hugging Face Inference API. The plugin evaluates ingredient safety based on user-provided information about allergies and skin types.
|
| 4 |
+
|
| 5 |
+
## Features
|
| 6 |
+
|
| 7 |
+
- Analyze shampoo ingredients using Microsoft Phi-3.5-mini-instruct model
|
| 8 |
+
- Extract ingredients from images using Microsoft Phi-3.5-vision-instruct model
|
| 9 |
+
- Input allergies and skin type for personalized recommendations
|
| 10 |
+
- Simple, user-friendly interface
|
| 11 |
+
- Test mode for trying the plugin without API calls
|
| 12 |
+
|
| 13 |
+
## Installation
|
| 14 |
+
|
| 15 |
+
1. Upload the `phi-shampoo-plugin` folder to your WordPress plugins directory (`/wp-content/plugins/`)
|
| 16 |
+
2. Activate the plugin through the 'Plugins' menu in WordPress
|
| 17 |
+
3. The plugin will automatically create a "Phi Shampoo Analyzer" page with the analyzer form
|
| 18 |
+
|
| 19 |
+
## Configuration
|
| 20 |
+
|
| 21 |
+
1. Go to "Phi Shampoo" in your WordPress admin menu
|
| 22 |
+
2. Enter your Hugging Face API token (get one at [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens))
|
| 23 |
+
3. Make sure your Hugging Face account has the Inference API enabled
|
| 24 |
+
4. Save your settings
|
| 25 |
+
|
| 26 |
+
> **Important**: Your API token requires at least "Read" access to use the Inference API. Never share your API token publicly.
|
| 27 |
+
|
| 28 |
+
## Usage
|
| 29 |
+
|
| 30 |
+
1. Go to the "Phi Shampoo Analyzer" page on your WordPress site
|
| 31 |
+
2. Enter shampoo ingredients manually OR upload an image of the ingredient list
|
| 32 |
+
3. (Optional) Enter any allergies you have
|
| 33 |
+
4. (Optional) Select your skin type
|
| 34 |
+
5. Click "Analyze" to get safety recommendations
|
| 35 |
+
|
| 36 |
+
## Technical Details
|
| 37 |
+
|
| 38 |
+
- Uses Microsoft Phi-3.5-mini-instruct for text analysis
|
| 39 |
+
- Uses Microsoft Phi-3.5-vision-instruct for image processing
|
| 40 |
+
- Connects to Hugging Face Inference API
|
| 41 |
+
- jQuery for form handling and UI interactions
|
| 42 |
+
- Responsive design
|
| 43 |
+
|
| 44 |
+
## Requirements
|
| 45 |
+
|
| 46 |
+
- WordPress 5.0 or higher
|
| 47 |
+
- PHP 7.2 or higher
|
| 48 |
+
- Active internet connection for API calls
|
| 49 |
+
- Hugging Face account with Inference API access
|
| 50 |
+
|
| 51 |
+
## Troubleshooting
|
| 52 |
+
|
| 53 |
+
If you're experiencing issues with the API connection:
|
| 54 |
+
|
| 55 |
+
1. Verify your API token is correctly entered in the plugin settings
|
| 56 |
+
2. Ensure your Hugging Face account has the Inference API enabled
|
| 57 |
+
3. Check that your token has at least "Read" permissions
|
| 58 |
+
4. Use the diagnostic tool available in the plugin settings page
|
| 59 |
+
|
| 60 |
+
## Screenshots
|
| 61 |
+
|
| 62 |
+
(Screenshots would be added here after plugin implementation)
|
| 63 |
+
|
| 64 |
+
## License
|
| 65 |
+
|
| 66 |
+
GPL v2 or later
|
assets/.DS_Store
ADDED
|
Binary file (6.15 kB). View file
|
|
|
assets/css/style.css
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Phi Shampoo Analyzer Styles */
|
| 2 |
+
|
| 3 |
+
.phi-shampoo-analyzer {
|
| 4 |
+
max-width: 800px;
|
| 5 |
+
margin: 0 auto;
|
| 6 |
+
padding: 20px;
|
| 7 |
+
background-color: #f9f9f9;
|
| 8 |
+
border-radius: 8px;
|
| 9 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
.phi-shampoo-analyzer h2 {
|
| 13 |
+
color: #333;
|
| 14 |
+
margin-bottom: 20px;
|
| 15 |
+
text-align: center;
|
| 16 |
+
font-size: 24px;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
.phi-form-group {
|
| 20 |
+
margin-bottom: 20px;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.phi-form-group label {
|
| 24 |
+
display: block;
|
| 25 |
+
margin-bottom: 5px;
|
| 26 |
+
font-weight: bold;
|
| 27 |
+
color: #555;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.phi-form-group textarea,
|
| 31 |
+
.phi-form-group input[type="text"],
|
| 32 |
+
.phi-form-group select {
|
| 33 |
+
width: 100%;
|
| 34 |
+
padding: 10px;
|
| 35 |
+
border: 1px solid #ddd;
|
| 36 |
+
border-radius: 4px;
|
| 37 |
+
background-color: #fff;
|
| 38 |
+
font-family: inherit;
|
| 39 |
+
font-size: 14px;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
.phi-form-group textarea {
|
| 43 |
+
min-height: 120px;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.phi-form-submit {
|
| 47 |
+
text-align: center;
|
| 48 |
+
margin-top: 20px;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.phi-submit-button {
|
| 52 |
+
background-color: #4a90e2;
|
| 53 |
+
color: white;
|
| 54 |
+
border: none;
|
| 55 |
+
padding: 12px 25px;
|
| 56 |
+
border-radius: 4px;
|
| 57 |
+
cursor: pointer;
|
| 58 |
+
font-size: 16px;
|
| 59 |
+
font-weight: bold;
|
| 60 |
+
transition: background-color 0.3s;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
.phi-submit-button:hover {
|
| 64 |
+
background-color: #357bd8;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
.phi-result {
|
| 68 |
+
margin-top: 30px;
|
| 69 |
+
padding: 20px;
|
| 70 |
+
background-color: #fff;
|
| 71 |
+
border-radius: 8px;
|
| 72 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
.phi-result h3 {
|
| 76 |
+
color: #333;
|
| 77 |
+
margin-bottom: 15px;
|
| 78 |
+
font-size: 20px;
|
| 79 |
+
border-bottom: 1px solid #eee;
|
| 80 |
+
padding-bottom: 10px;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.phi-analysis-content {
|
| 84 |
+
white-space: pre-line;
|
| 85 |
+
line-height: 1.6;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.phi-error {
|
| 89 |
+
color: #e74c3c;
|
| 90 |
+
font-weight: bold;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
.phi-loading {
|
| 94 |
+
text-align: center;
|
| 95 |
+
margin: 20px 0;
|
| 96 |
+
padding: 15px;
|
| 97 |
+
background-color: #f5f5f5;
|
| 98 |
+
border-radius: 4px;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
/* Responsive adjustments */
|
| 102 |
+
@media (max-width: 600px) {
|
| 103 |
+
.phi-shampoo-analyzer {
|
| 104 |
+
padding: 15px;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
.phi-submit-button {
|
| 108 |
+
width: 100%;
|
| 109 |
+
}
|
| 110 |
+
}
|
assets/js/phi-shampoo.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Phi Shampoo Analyzer JavaScript
|
| 3 |
+
*/
|
| 4 |
+
(function($) {
|
| 5 |
+
'use strict';
|
| 6 |
+
|
| 7 |
+
$(document).ready(function() {
|
| 8 |
+
// The form submit handler is already in the shortcode,
|
| 9 |
+
// but we can add additional functionality here
|
| 10 |
+
|
| 11 |
+
// Toggle between text input and image upload
|
| 12 |
+
$('#phi-image').on('change', function() {
|
| 13 |
+
if ($(this).val()) {
|
| 14 |
+
// If an image is selected, disable the ingredients textarea
|
| 15 |
+
$('#phi-ingredients').prop('disabled', true).css('opacity', '0.5');
|
| 16 |
+
} else {
|
| 17 |
+
// If no image is selected, enable the ingredients textarea
|
| 18 |
+
$('#phi-ingredients').prop('disabled', false).css('opacity', '1');
|
| 19 |
+
}
|
| 20 |
+
});
|
| 21 |
+
|
| 22 |
+
$('#phi-ingredients').on('input', function() {
|
| 23 |
+
if ($(this).val()) {
|
| 24 |
+
// If ingredients are entered, disable the image upload
|
| 25 |
+
$('#phi-image').prop('disabled', true).css('opacity', '0.5');
|
| 26 |
+
} else {
|
| 27 |
+
// If no ingredients are entered, enable the image upload
|
| 28 |
+
$('#phi-image').prop('disabled', false).css('opacity', '1');
|
| 29 |
+
}
|
| 30 |
+
});
|
| 31 |
+
});
|
| 32 |
+
|
| 33 |
+
})(jQuery);
|
diagnostic.php
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* Phi Shampoo Analyzer Diagnostic Tool
|
| 4 |
+
*
|
| 5 |
+
* This file can be accessed directly to test API connectivity and plugin functionality.
|
| 6 |
+
* URL: your-site.com/wp-content/plugins/phi-shampoo-plugin/diagnostic.php
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
// Test if WordPress is loaded
|
| 10 |
+
if (file_exists('../../../../wp-load.php')) {
|
| 11 |
+
require_once('../../../../wp-load.php');
|
| 12 |
+
} else {
|
| 13 |
+
die('Could not find WordPress. Make sure this file is in the plugin directory.');
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
// Security check - only admins can access
|
| 17 |
+
if (!current_user_can('manage_options')) {
|
| 18 |
+
die('You do not have permission to access this page.');
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
// Load plugin constants
|
| 22 |
+
if (!defined('PHI_SHAMPOO_TEXT_MODEL')) {
|
| 23 |
+
if (file_exists('./phi-shampoo-analyzer.php')) {
|
| 24 |
+
include_once('./phi-shampoo-analyzer.php');
|
| 25 |
+
} else {
|
| 26 |
+
die('Could not find the main plugin file.');
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
// Set up the page
|
| 31 |
+
?>
|
| 32 |
+
<!DOCTYPE html>
|
| 33 |
+
<html>
|
| 34 |
+
<head>
|
| 35 |
+
<meta charset="utf-8">
|
| 36 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 37 |
+
<title>Phi Shampoo Analyzer Diagnostic</title>
|
| 38 |
+
<style>
|
| 39 |
+
body {
|
| 40 |
+
font-family: Arial, sans-serif;
|
| 41 |
+
line-height: 1.6;
|
| 42 |
+
margin: 20px;
|
| 43 |
+
max-width: 900px;
|
| 44 |
+
margin: 0 auto;
|
| 45 |
+
padding: 20px;
|
| 46 |
+
}
|
| 47 |
+
h1, h2 {
|
| 48 |
+
color: #333;
|
| 49 |
+
}
|
| 50 |
+
pre {
|
| 51 |
+
background-color: #f5f5f5;
|
| 52 |
+
padding: 10px;
|
| 53 |
+
border-radius: 4px;
|
| 54 |
+
overflow-x: auto;
|
| 55 |
+
}
|
| 56 |
+
.test-section {
|
| 57 |
+
margin-bottom: 30px;
|
| 58 |
+
padding: 15px;
|
| 59 |
+
border: 1px solid #ddd;
|
| 60 |
+
border-radius: 4px;
|
| 61 |
+
}
|
| 62 |
+
.success {
|
| 63 |
+
color: green;
|
| 64 |
+
font-weight: bold;
|
| 65 |
+
}
|
| 66 |
+
.error {
|
| 67 |
+
color: red;
|
| 68 |
+
font-weight: bold;
|
| 69 |
+
}
|
| 70 |
+
</style>
|
| 71 |
+
</head>
|
| 72 |
+
<body>
|
| 73 |
+
<h1>Phi Shampoo Analyzer Diagnostic Tool</h1>
|
| 74 |
+
|
| 75 |
+
<div class="test-section">
|
| 76 |
+
<h2>Plugin Information</h2>
|
| 77 |
+
<p><strong>Plugin Version:</strong> <?php echo defined('PHI_SHAMPOO_VERSION') ? PHI_SHAMPOO_VERSION : 'Not defined'; ?></p>
|
| 78 |
+
<p><strong>Text Model:</strong> <?php echo defined('PHI_SHAMPOO_TEXT_MODEL') ? PHI_SHAMPOO_TEXT_MODEL : 'Not defined'; ?></p>
|
| 79 |
+
<p><strong>Vision Model:</strong> <?php echo defined('PHI_SHAMPOO_VISION_MODEL') ? PHI_SHAMPOO_VISION_MODEL : 'Not defined'; ?></p>
|
| 80 |
+
<p><strong>Text API URL:</strong> <?php echo defined('PHI_SHAMPOO_HF_TEXT_API_URL') ? PHI_SHAMPOO_HF_TEXT_API_URL : 'Not defined'; ?></p>
|
| 81 |
+
<p><strong>Vision API URL:</strong> <?php echo defined('PHI_SHAMPOO_HF_VISION_API_URL') ? PHI_SHAMPOO_HF_VISION_API_URL : 'Not defined'; ?></p>
|
| 82 |
+
<p><strong>API Token:</strong> <?php
|
| 83 |
+
$api_token = function_exists('phi_shampoo_get_api_token') ? phi_shampoo_get_api_token() : '';
|
| 84 |
+
echo !empty($api_token) ? 'Configured' : '<span class="error">Not configured</span>';
|
| 85 |
+
?></p>
|
| 86 |
+
</div>
|
| 87 |
+
|
| 88 |
+
<div class="test-section">
|
| 89 |
+
<h2>WordPress Environment</h2>
|
| 90 |
+
<p><strong>WordPress Version:</strong> <?php echo get_bloginfo('version'); ?></p>
|
| 91 |
+
<p><strong>PHP Version:</strong> <?php echo phpversion(); ?></p>
|
| 92 |
+
<p><strong>Analyzer Page:</strong>
|
| 93 |
+
<?php
|
| 94 |
+
$page_id = get_option('phi_shampoo_analyzer_page_id');
|
| 95 |
+
if ($page_id) {
|
| 96 |
+
echo 'Created (ID: ' . $page_id . ') - <a href="' . get_permalink($page_id) . '" target="_blank">View Page</a>';
|
| 97 |
+
} else {
|
| 98 |
+
echo '<span class="error">Not created</span>';
|
| 99 |
+
}
|
| 100 |
+
?>
|
| 101 |
+
</p>
|
| 102 |
+
</div>
|
| 103 |
+
|
| 104 |
+
<div class="test-section">
|
| 105 |
+
<h2>API Connection Test</h2>
|
| 106 |
+
|
| 107 |
+
<h3>Text Model Test</h3>
|
| 108 |
+
<?php
|
| 109 |
+
// Test the text model API
|
| 110 |
+
if (!defined('PHI_SHAMPOO_HF_TEXT_API_URL') || !function_exists('phi_shampoo_get_api_token')) {
|
| 111 |
+
echo '<p class="error">API functions not defined.</p>';
|
| 112 |
+
} else {
|
| 113 |
+
$api_url = PHI_SHAMPOO_HF_TEXT_API_URL;
|
| 114 |
+
$api_token = phi_shampoo_get_api_token();
|
| 115 |
+
|
| 116 |
+
if (empty($api_token)) {
|
| 117 |
+
echo '<p class="error">API token not configured. Please go to the <a href="' . admin_url('admin.php?page=phi-shampoo-settings') . '">Settings Page</a> to configure your token.</p>';
|
| 118 |
+
} else {
|
| 119 |
+
$response = wp_remote_post($api_url, array(
|
| 120 |
+
'headers' => array(
|
| 121 |
+
'Authorization' => 'Bearer ' . $api_token,
|
| 122 |
+
'Content-Type' => 'application/json'
|
| 123 |
+
),
|
| 124 |
+
'body' => json_encode(array(
|
| 125 |
+
'inputs' => 'Test prompt: Analyze these ingredients: Water, Glycerin',
|
| 126 |
+
'parameters' => array(
|
| 127 |
+
'max_new_tokens' => 50,
|
| 128 |
+
'temperature' => 0.7
|
| 129 |
+
)
|
| 130 |
+
)),
|
| 131 |
+
'timeout' => 30
|
| 132 |
+
));
|
| 133 |
+
|
| 134 |
+
if (is_wp_error($response)) {
|
| 135 |
+
echo '<p class="error">Error: ' . $response->get_error_message() . '</p>';
|
| 136 |
+
} else {
|
| 137 |
+
$status = wp_remote_retrieve_response_code($response);
|
| 138 |
+
$body = wp_remote_retrieve_body($response);
|
| 139 |
+
|
| 140 |
+
echo '<p><strong>Status Code:</strong> ' . $status . '</p>';
|
| 141 |
+
|
| 142 |
+
if ($status === 200) {
|
| 143 |
+
echo '<p class="success">Text API connection successful!</p>';
|
| 144 |
+
echo '<p><strong>Response Preview:</strong></p>';
|
| 145 |
+
echo '<pre>' . htmlspecialchars(substr($body, 0, 300)) . '...</pre>';
|
| 146 |
+
} else {
|
| 147 |
+
echo '<p class="error">API returned non-200 status code.</p>';
|
| 148 |
+
echo '<p><strong>Response:</strong></p>';
|
| 149 |
+
echo '<pre>' . htmlspecialchars($body) . '</pre>';
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
?>
|
| 155 |
+
|
| 156 |
+
<h3>Vision Model Test</h3>
|
| 157 |
+
<p>Note: Vision model test requires an image upload. To test, please use the main plugin interface.</p>
|
| 158 |
+
</div>
|
| 159 |
+
|
| 160 |
+
<div class="test-section">
|
| 161 |
+
<h2>File Structure Check</h2>
|
| 162 |
+
<?php
|
| 163 |
+
$required_files = array(
|
| 164 |
+
'phi-shampoo-analyzer.php' => 'Main plugin file',
|
| 165 |
+
'assets/css/style.css' => 'CSS styles',
|
| 166 |
+
'assets/js/phi-shampoo.js' => 'JavaScript functionality'
|
| 167 |
+
);
|
| 168 |
+
|
| 169 |
+
$all_files_exist = true;
|
| 170 |
+
|
| 171 |
+
foreach ($required_files as $file => $description) {
|
| 172 |
+
$file_path = dirname(__FILE__) . '/' . $file;
|
| 173 |
+
$exists = file_exists($file_path);
|
| 174 |
+
|
| 175 |
+
echo '<p>';
|
| 176 |
+
echo '<strong>' . $file . ':</strong> ';
|
| 177 |
+
if ($exists) {
|
| 178 |
+
echo '<span class="success">Found</span>';
|
| 179 |
+
} else {
|
| 180 |
+
echo '<span class="error">Missing</span>';
|
| 181 |
+
$all_files_exist = false;
|
| 182 |
+
}
|
| 183 |
+
echo ' - ' . $description;
|
| 184 |
+
echo '</p>';
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
if ($all_files_exist) {
|
| 188 |
+
echo '<p class="success">All required files are present.</p>';
|
| 189 |
+
} else {
|
| 190 |
+
echo '<p class="error">Some required files are missing.</p>';
|
| 191 |
+
}
|
| 192 |
+
?>
|
| 193 |
+
</div>
|
| 194 |
+
</body>
|
| 195 |
+
</html>
|
phi-shampoo-analyzer.php
ADDED
|
@@ -0,0 +1,658 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/*
|
| 3 |
+
Plugin Name: Phi Shampoo Analyzer
|
| 4 |
+
Plugin URI: https://example.com/
|
| 5 |
+
Description: Uses Microsoft Phi-3.5 models via Hugging Face Inference API for shampoo ingredient analysis.
|
| 6 |
+
Version: 1.0
|
| 7 |
+
Author: Monica Hsueh
|
| 8 |
+
License: GPL2
|
| 9 |
+
*/
|
| 10 |
+
|
| 11 |
+
/*
|
| 12 |
+
Model Information:
|
| 13 |
+
--------------------------
|
| 14 |
+
Text Model: microsoft/Phi-3.5-mini-instruct
|
| 15 |
+
Vision Model: microsoft/Phi-3.5-vision-instruct
|
| 16 |
+
*/
|
| 17 |
+
|
| 18 |
+
// Prevent direct access
|
| 19 |
+
if (!defined('ABSPATH')) {
|
| 20 |
+
exit;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
// Define plugin constants
|
| 24 |
+
define('PHI_SHAMPOO_VERSION', '1.0');
|
| 25 |
+
define('PHI_SHAMPOO_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
| 26 |
+
define('PHI_SHAMPOO_PLUGIN_URL', plugin_dir_url(__FILE__));
|
| 27 |
+
define('PHI_SHAMPOO_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
| 28 |
+
|
| 29 |
+
// Define model constants
|
| 30 |
+
define('PHI_SHAMPOO_TEXT_MODEL', 'microsoft/Phi-3.5-mini-instruct');
|
| 31 |
+
define('PHI_SHAMPOO_VISION_MODEL', 'microsoft/Phi-3.5-vision-instruct');
|
| 32 |
+
define('PHI_SHAMPOO_HF_TEXT_API_URL', 'https://api-inference.huggingface.co/models/' . PHI_SHAMPOO_TEXT_MODEL);
|
| 33 |
+
define('PHI_SHAMPOO_HF_VISION_API_URL', 'https://api-inference.huggingface.co/models/' . PHI_SHAMPOO_VISION_MODEL);
|
| 34 |
+
|
| 35 |
+
/**
|
| 36 |
+
* Plugin activation hook
|
| 37 |
+
*/
|
| 38 |
+
function phi_shampoo_activate() {
|
| 39 |
+
// Create default pages with shortcodes
|
| 40 |
+
phi_shampoo_create_pages();
|
| 41 |
+
|
| 42 |
+
// Set default settings
|
| 43 |
+
if (!get_option('phi_shampoo_test_mode')) {
|
| 44 |
+
update_option('phi_shampoo_test_mode', true); // Setting to true for initial testing
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// Flush rewrite rules
|
| 48 |
+
flush_rewrite_rules();
|
| 49 |
+
}
|
| 50 |
+
register_activation_hook(__FILE__, 'phi_shampoo_activate');
|
| 51 |
+
|
| 52 |
+
/**
|
| 53 |
+
* Plugin deactivation hook
|
| 54 |
+
*/
|
| 55 |
+
function phi_shampoo_deactivate() {
|
| 56 |
+
// Flush rewrite rules on deactivation
|
| 57 |
+
flush_rewrite_rules();
|
| 58 |
+
}
|
| 59 |
+
register_deactivation_hook(__FILE__, 'phi_shampoo_deactivate');
|
| 60 |
+
|
| 61 |
+
/**
|
| 62 |
+
* Create required pages with shortcodes
|
| 63 |
+
*/
|
| 64 |
+
function phi_shampoo_create_pages() {
|
| 65 |
+
// Create analyzer page if it doesn't exist
|
| 66 |
+
$analyzer_page_id = wp_insert_post(array(
|
| 67 |
+
'post_title' => 'Phi Shampoo Analyzer',
|
| 68 |
+
'post_content' => '[phi_shampoo_analyzer]',
|
| 69 |
+
'post_status' => 'publish',
|
| 70 |
+
'post_type' => 'page',
|
| 71 |
+
'comment_status' => 'closed'
|
| 72 |
+
));
|
| 73 |
+
|
| 74 |
+
// Save page ID to settings
|
| 75 |
+
update_option('phi_shampoo_analyzer_page_id', $analyzer_page_id);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
/**
|
| 79 |
+
* Enqueue scripts and styles
|
| 80 |
+
*/
|
| 81 |
+
function phi_shampoo_enqueue_scripts() {
|
| 82 |
+
// Register and enqueue CSS
|
| 83 |
+
wp_register_style('phi-shampoo-style', PHI_SHAMPOO_PLUGIN_URL . 'assets/css/style.css', array(), PHI_SHAMPOO_VERSION);
|
| 84 |
+
wp_enqueue_style('phi-shampoo-style');
|
| 85 |
+
|
| 86 |
+
// Register and enqueue JavaScript
|
| 87 |
+
wp_register_script('phi-shampoo-script', PHI_SHAMPOO_PLUGIN_URL . 'assets/js/phi-shampoo.js', array('jquery'), PHI_SHAMPOO_VERSION, true);
|
| 88 |
+
wp_enqueue_script('phi-shampoo-script');
|
| 89 |
+
|
| 90 |
+
// Localize the script with the ajax URL
|
| 91 |
+
wp_localize_script('phi-shampoo-script', 'phiShampoo', array(
|
| 92 |
+
'ajax_url' => admin_url('admin-ajax.php'),
|
| 93 |
+
'nonce' => wp_create_nonce('phi_shampoo_nonce'),
|
| 94 |
+
'analyzePage' => get_permalink(get_option('phi_shampoo_analyzer_page_id'))
|
| 95 |
+
));
|
| 96 |
+
}
|
| 97 |
+
add_action('wp_enqueue_scripts', 'phi_shampoo_enqueue_scripts');
|
| 98 |
+
|
| 99 |
+
/**
|
| 100 |
+
* Write to debug log
|
| 101 |
+
*/
|
| 102 |
+
function phi_shampoo_log($message) {
|
| 103 |
+
if (defined('WP_DEBUG') && WP_DEBUG) {
|
| 104 |
+
$log_file = WP_CONTENT_DIR . '/debug.log';
|
| 105 |
+
$timestamp = current_time('mysql');
|
| 106 |
+
if (is_array($message) || is_object($message)) {
|
| 107 |
+
error_log('[' . $timestamp . '] Phi Shampoo: ' . print_r($message, true) . "\n", 3, $log_file);
|
| 108 |
+
} else {
|
| 109 |
+
error_log('[' . $timestamp . '] Phi Shampoo: ' . $message . "\n", 3, $log_file);
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
/**
|
| 115 |
+
* Add settings link to plugins page
|
| 116 |
+
*/
|
| 117 |
+
function phi_shampoo_add_settings_link($links) {
|
| 118 |
+
$settings_link = '<a href="' . admin_url('admin.php?page=phi-shampoo-settings') . '">' . __('Settings', 'phi-shampoo-analyzer') . '</a>';
|
| 119 |
+
array_unshift($links, $settings_link);
|
| 120 |
+
return $links;
|
| 121 |
+
}
|
| 122 |
+
add_filter('plugin_action_links_' . PHI_SHAMPOO_PLUGIN_BASENAME, 'phi_shampoo_add_settings_link');
|
| 123 |
+
|
| 124 |
+
/**
|
| 125 |
+
* Register shortcodes
|
| 126 |
+
*/
|
| 127 |
+
function phi_shampoo_register_shortcodes() {
|
| 128 |
+
add_shortcode('phi_shampoo_analyzer', 'phi_shampoo_analyzer_shortcode');
|
| 129 |
+
}
|
| 130 |
+
add_action('init', 'phi_shampoo_register_shortcodes');
|
| 131 |
+
|
| 132 |
+
/**
|
| 133 |
+
* Shampoo analyzer shortcode
|
| 134 |
+
*/
|
| 135 |
+
function phi_shampoo_analyzer_shortcode() {
|
| 136 |
+
ob_start();
|
| 137 |
+
?>
|
| 138 |
+
<div class="phi-shampoo-analyzer">
|
| 139 |
+
<h2>Phi Shampoo Analyzer</h2>
|
| 140 |
+
<p>Enter shampoo ingredients below or upload an image of the ingredient list.</p>
|
| 141 |
+
|
| 142 |
+
<form id="phi-analyzer-form" method="post" enctype="multipart/form-data">
|
| 143 |
+
<?php wp_nonce_field('phi_shampoo_analyze', 'phi_shampoo_nonce'); ?>
|
| 144 |
+
|
| 145 |
+
<div class="phi-form-group">
|
| 146 |
+
<label for="phi-ingredients">Ingredients:</label>
|
| 147 |
+
<textarea id="phi-ingredients" name="ingredients" rows="5" placeholder="Enter ingredients separated by commas..."></textarea>
|
| 148 |
+
</div>
|
| 149 |
+
|
| 150 |
+
<div class="phi-form-group">
|
| 151 |
+
<label for="phi-image">Or upload image:</label>
|
| 152 |
+
<input type="file" id="phi-image" name="ingredient_image" accept="image/*">
|
| 153 |
+
</div>
|
| 154 |
+
|
| 155 |
+
<div class="phi-form-group">
|
| 156 |
+
<label for="phi-allergies">Known Allergies (optional):</label>
|
| 157 |
+
<input type="text" id="phi-allergies" name="allergies" placeholder="e.g., Parabens, Sulfates, Fragrances">
|
| 158 |
+
</div>
|
| 159 |
+
|
| 160 |
+
<div class="phi-form-group">
|
| 161 |
+
<label for="phi-skin-type">Skin Type (optional):</label>
|
| 162 |
+
<select id="phi-skin-type" name="skin_type">
|
| 163 |
+
<option value="">Select Skin Type</option>
|
| 164 |
+
<option value="Normal">Normal</option>
|
| 165 |
+
<option value="Dry">Dry</option>
|
| 166 |
+
<option value="Oily">Oily</option>
|
| 167 |
+
<option value="Combination">Combination</option>
|
| 168 |
+
<option value="Sensitive">Sensitive</option>
|
| 169 |
+
</select>
|
| 170 |
+
</div>
|
| 171 |
+
|
| 172 |
+
<div class="phi-form-submit">
|
| 173 |
+
<button type="submit" class="phi-submit-button">Analyze</button>
|
| 174 |
+
</div>
|
| 175 |
+
</form>
|
| 176 |
+
|
| 177 |
+
<div id="phi-result" class="phi-result" style="display: none;">
|
| 178 |
+
<h3>Analysis Result</h3>
|
| 179 |
+
<div id="phi-result-content"></div>
|
| 180 |
+
</div>
|
| 181 |
+
|
| 182 |
+
<div id="phi-loading" class="phi-loading" style="display: none;">
|
| 183 |
+
<p>Analyzing ingredients... Please wait.</p>
|
| 184 |
+
</div>
|
| 185 |
+
</div>
|
| 186 |
+
|
| 187 |
+
<script>
|
| 188 |
+
jQuery(document).ready(function($) {
|
| 189 |
+
$('#phi-analyzer-form').on('submit', function(e) {
|
| 190 |
+
e.preventDefault();
|
| 191 |
+
|
| 192 |
+
$('#phi-loading').show();
|
| 193 |
+
$('#phi-result').hide();
|
| 194 |
+
|
| 195 |
+
var formData = new FormData(this);
|
| 196 |
+
formData.append('action', 'phi_analyze_shampoo');
|
| 197 |
+
|
| 198 |
+
$.ajax({
|
| 199 |
+
url: phiShampoo.ajax_url,
|
| 200 |
+
type: 'POST',
|
| 201 |
+
data: formData,
|
| 202 |
+
contentType: false,
|
| 203 |
+
processData: false,
|
| 204 |
+
success: function(response) {
|
| 205 |
+
$('#phi-loading').hide();
|
| 206 |
+
|
| 207 |
+
if (response.success) {
|
| 208 |
+
$('#phi-result-content').html(response.data.result);
|
| 209 |
+
$('#phi-result').show();
|
| 210 |
+
} else {
|
| 211 |
+
$('#phi-result-content').html('<p class="phi-error">Error: ' + response.data.message + '</p>');
|
| 212 |
+
$('#phi-result').show();
|
| 213 |
+
}
|
| 214 |
+
},
|
| 215 |
+
error: function() {
|
| 216 |
+
$('#phi-loading').hide();
|
| 217 |
+
$('#phi-result-content').html('<p class="phi-error">Error: Could not connect to the server.</p>');
|
| 218 |
+
$('#phi-result').show();
|
| 219 |
+
}
|
| 220 |
+
});
|
| 221 |
+
});
|
| 222 |
+
});
|
| 223 |
+
</script>
|
| 224 |
+
<?php
|
| 225 |
+
return ob_get_clean();
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
/**
|
| 229 |
+
* AJAX handler for shampoo analysis
|
| 230 |
+
*/
|
| 231 |
+
function phi_shampoo_ajax_analyze() {
|
| 232 |
+
// Check nonce
|
| 233 |
+
if (!isset($_POST['phi_shampoo_nonce']) || !wp_verify_nonce($_POST['phi_shampoo_nonce'], 'phi_shampoo_analyze')) {
|
| 234 |
+
wp_send_json_error(array('message' => 'Security check failed.'));
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
$ingredients = '';
|
| 238 |
+
$image_uploaded = false;
|
| 239 |
+
|
| 240 |
+
// Handle image upload
|
| 241 |
+
if (!empty($_FILES['ingredient_image']['tmp_name'])) {
|
| 242 |
+
$image_uploaded = true;
|
| 243 |
+
$tmp_name = $_FILES['ingredient_image']['tmp_name'];
|
| 244 |
+
|
| 245 |
+
// Process image to extract ingredients
|
| 246 |
+
$result = phi_shampoo_process_image($tmp_name);
|
| 247 |
+
|
| 248 |
+
if ($result['success']) {
|
| 249 |
+
$ingredients = $result['ingredients'];
|
| 250 |
+
} else {
|
| 251 |
+
wp_send_json_error(array('message' => 'Could not extract ingredients from image: ' . $result['error']));
|
| 252 |
+
}
|
| 253 |
+
} else if (!empty($_POST['ingredients'])) {
|
| 254 |
+
$ingredients = sanitize_textarea_field($_POST['ingredients']);
|
| 255 |
+
} else {
|
| 256 |
+
wp_send_json_error(array('message' => 'Please provide ingredients or upload an image.'));
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
// Gather additional data
|
| 260 |
+
$allergies = isset($_POST['allergies']) ? sanitize_text_field($_POST['allergies']) : '';
|
| 261 |
+
$skin_type = isset($_POST['skin_type']) ? sanitize_text_field($_POST['skin_type']) : '';
|
| 262 |
+
|
| 263 |
+
// Analyze ingredients
|
| 264 |
+
$analysis_data = array(
|
| 265 |
+
'ingredients' => $ingredients,
|
| 266 |
+
'allergies' => $allergies,
|
| 267 |
+
'skin_type' => $skin_type
|
| 268 |
+
);
|
| 269 |
+
|
| 270 |
+
$analysis = phi_shampoo_analyze_ingredients($analysis_data);
|
| 271 |
+
|
| 272 |
+
if ($analysis['success']) {
|
| 273 |
+
$html_result = '<div class="phi-analysis">';
|
| 274 |
+
|
| 275 |
+
if ($image_uploaded) {
|
| 276 |
+
$html_result .= '<p><strong>Extracted Ingredients:</strong> ' . esc_html($ingredients) . '</p>';
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
$html_result .= '<div class="phi-analysis-content">' . nl2br(esc_html($analysis['result'])) . '</div>';
|
| 280 |
+
$html_result .= '</div>';
|
| 281 |
+
|
| 282 |
+
wp_send_json_success(array('result' => $html_result));
|
| 283 |
+
} else {
|
| 284 |
+
wp_send_json_error(array('message' => 'Analysis failed: ' . $analysis['error']));
|
| 285 |
+
}
|
| 286 |
+
}
|
| 287 |
+
add_action('wp_ajax_phi_analyze_shampoo', 'phi_shampoo_ajax_analyze');
|
| 288 |
+
add_action('wp_ajax_nopriv_phi_analyze_shampoo', 'phi_shampoo_ajax_analyze');
|
| 289 |
+
|
| 290 |
+
/**
|
| 291 |
+
* Process image to extract ingredients using Phi-3.5 Vision
|
| 292 |
+
*/
|
| 293 |
+
function phi_shampoo_process_image($image_path) {
|
| 294 |
+
// Check if in test mode
|
| 295 |
+
$test_mode = get_option('phi_shampoo_test_mode', false);
|
| 296 |
+
if ($test_mode) {
|
| 297 |
+
phi_shampoo_log("Test mode active - returning test ingredients from image");
|
| 298 |
+
return array(
|
| 299 |
+
'success' => true,
|
| 300 |
+
'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"
|
| 301 |
+
);
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
// Get API token
|
| 305 |
+
$api_token = phi_shampoo_get_api_token();
|
| 306 |
+
|
| 307 |
+
if (empty($api_token)) {
|
| 308 |
+
phi_shampoo_log("Error: Missing API token for Hugging Face");
|
| 309 |
+
return array(
|
| 310 |
+
'success' => false,
|
| 311 |
+
'error' => 'Missing API token. Please configure it in plugin settings.',
|
| 312 |
+
'ingredients' => ''
|
| 313 |
+
);
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
// Check if file exists
|
| 317 |
+
if (!file_exists($image_path)) {
|
| 318 |
+
phi_shampoo_log("Error: Image file not found at path: " . $image_path);
|
| 319 |
+
return array(
|
| 320 |
+
'success' => false,
|
| 321 |
+
'error' => 'Image file not found.',
|
| 322 |
+
'ingredients' => ''
|
| 323 |
+
);
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
// Read and encode image
|
| 327 |
+
$image_data = file_get_contents($image_path);
|
| 328 |
+
$base64_image = base64_encode($image_data);
|
| 329 |
+
|
| 330 |
+
// Make API request
|
| 331 |
+
$api_url = defined('PHI_SHAMPOO_HF_VISION_API_URL') ? PHI_SHAMPOO_HF_VISION_API_URL : '';
|
| 332 |
+
|
| 333 |
+
$response = wp_remote_post($api_url, array(
|
| 334 |
+
'headers' => array(
|
| 335 |
+
'Authorization' => 'Bearer ' . $api_token,
|
| 336 |
+
'Content-Type' => 'application/json'
|
| 337 |
+
),
|
| 338 |
+
'body' => json_encode(array(
|
| 339 |
+
'inputs' => array(
|
| 340 |
+
'image' => $base64_image,
|
| 341 |
+
'text' => "Extract all the ingredients from this shampoo product label. Format them as a comma-separated list."
|
| 342 |
+
)
|
| 343 |
+
)),
|
| 344 |
+
'timeout' => 60
|
| 345 |
+
));
|
| 346 |
+
|
| 347 |
+
// Handle response
|
| 348 |
+
if (is_wp_error($response)) {
|
| 349 |
+
phi_shampoo_log("API Error: " . $response->get_error_message());
|
| 350 |
+
return array(
|
| 351 |
+
'success' => false,
|
| 352 |
+
'error' => $response->get_error_message(),
|
| 353 |
+
'ingredients' => ''
|
| 354 |
+
);
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
$body = wp_remote_retrieve_body($response);
|
| 358 |
+
$status_code = wp_remote_retrieve_response_code($response);
|
| 359 |
+
|
| 360 |
+
phi_shampoo_log("API Status: " . $status_code . ", Response: " . substr($body, 0, 200) . "...");
|
| 361 |
+
|
| 362 |
+
if ($status_code !== 200) {
|
| 363 |
+
$error_message = "API Error: Received status code " . $status_code;
|
| 364 |
+
|
| 365 |
+
$decoded = json_decode($body, true);
|
| 366 |
+
if ($decoded && isset($decoded['error'])) {
|
| 367 |
+
$error_message .= " - " . $decoded['error'];
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
return array(
|
| 371 |
+
'success' => false,
|
| 372 |
+
'error' => $error_message,
|
| 373 |
+
'ingredients' => ''
|
| 374 |
+
);
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
$decoded = json_decode($body, true);
|
| 378 |
+
|
| 379 |
+
if (isset($decoded['generated_text'])) {
|
| 380 |
+
return array(
|
| 381 |
+
'success' => true,
|
| 382 |
+
'ingredients' => $decoded['generated_text']
|
| 383 |
+
);
|
| 384 |
+
} else {
|
| 385 |
+
return array(
|
| 386 |
+
'success' => false,
|
| 387 |
+
'error' => 'Unexpected response format',
|
| 388 |
+
'ingredients' => ''
|
| 389 |
+
);
|
| 390 |
+
}
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
/**
|
| 394 |
+
* Analyze ingredients using Phi-3.5 Text model
|
| 395 |
+
*/
|
| 396 |
+
function phi_shampoo_analyze_ingredients($data) {
|
| 397 |
+
// Check if in test mode
|
| 398 |
+
$test_mode = get_option('phi_shampoo_test_mode', false);
|
| 399 |
+
if ($test_mode) {
|
| 400 |
+
return phi_shampoo_generate_test_analysis($data);
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
// Get API token
|
| 404 |
+
$api_token = phi_shampoo_get_api_token();
|
| 405 |
+
|
| 406 |
+
if (empty($api_token)) {
|
| 407 |
+
return array(
|
| 408 |
+
'success' => false,
|
| 409 |
+
'error' => 'Missing API token. Please configure it in plugin settings.',
|
| 410 |
+
'result' => 'Error: API token not configured.'
|
| 411 |
+
);
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
// Check if ingredients are provided
|
| 415 |
+
if (empty($data['ingredients'])) {
|
| 416 |
+
return array(
|
| 417 |
+
'success' => false,
|
| 418 |
+
'error' => 'No ingredients provided for analysis.',
|
| 419 |
+
'result' => 'Error: No ingredients to analyze.'
|
| 420 |
+
);
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
// Build prompt for the model
|
| 424 |
+
$prompt = "You are a shampoo ingredient analyzer. Please analyze the following shampoo ingredients for safety and potential allergic reactions:\n\n";
|
| 425 |
+
$prompt .= "Ingredients:\n" . $data['ingredients'] . "\n\n";
|
| 426 |
+
|
| 427 |
+
// Add user information if available
|
| 428 |
+
if (!empty($data['allergies'])) {
|
| 429 |
+
$prompt .= "User has allergies to: " . $data['allergies'] . "\n\n";
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
if (!empty($data['skin_type'])) {
|
| 433 |
+
$prompt .= "User has " . $data['skin_type'] . " skin type.\n\n";
|
| 434 |
+
}
|
| 435 |
+
|
| 436 |
+
// Add analysis instructions
|
| 437 |
+
$prompt .= "Please provide a detailed analysis including:\n";
|
| 438 |
+
$prompt .= "1. Overall safety assessment (safe or not safe for this user)\n";
|
| 439 |
+
$prompt .= "2. Potentially harmful ingredients with explanations\n";
|
| 440 |
+
$prompt .= "3. Specific concerns based on the user's health profile\n";
|
| 441 |
+
$prompt .= "4. Alternative recommendations if needed\n";
|
| 442 |
+
$prompt .= "\nBegin your response with a clear 'SAFE TO USE' or 'NOT RECOMMENDED' statement.\n";
|
| 443 |
+
|
| 444 |
+
// Log the prompt
|
| 445 |
+
phi_shampoo_log("Analysis prompt: " . $prompt);
|
| 446 |
+
|
| 447 |
+
// Make API request
|
| 448 |
+
$api_url = defined('PHI_SHAMPOO_HF_TEXT_API_URL') ? PHI_SHAMPOO_HF_TEXT_API_URL : '';
|
| 449 |
+
|
| 450 |
+
$response = wp_remote_post($api_url, array(
|
| 451 |
+
'headers' => array(
|
| 452 |
+
'Authorization' => 'Bearer ' . $api_token,
|
| 453 |
+
'Content-Type' => 'application/json'
|
| 454 |
+
),
|
| 455 |
+
'body' => json_encode(array(
|
| 456 |
+
'inputs' => $prompt,
|
| 457 |
+
'parameters' => array(
|
| 458 |
+
'max_new_tokens' => 512,
|
| 459 |
+
'temperature' => 0.7,
|
| 460 |
+
'do_sample' => true
|
| 461 |
+
)
|
| 462 |
+
)),
|
| 463 |
+
'timeout' => 60
|
| 464 |
+
));
|
| 465 |
+
|
| 466 |
+
// Handle response
|
| 467 |
+
if (is_wp_error($response)) {
|
| 468 |
+
phi_shampoo_log("API Error: " . $response->get_error_message());
|
| 469 |
+
return array(
|
| 470 |
+
'success' => false,
|
| 471 |
+
'error' => $response->get_error_message(),
|
| 472 |
+
'result' => 'Error: API request failed. ' . $response->get_error_message()
|
| 473 |
+
);
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
$body = wp_remote_retrieve_body($response);
|
| 477 |
+
$status_code = wp_remote_retrieve_response_code($response);
|
| 478 |
+
|
| 479 |
+
phi_shampoo_log("API Status: " . $status_code . ", Response: " . substr($body, 0, 200) . "...");
|
| 480 |
+
|
| 481 |
+
if ($status_code !== 200) {
|
| 482 |
+
$error_message = "API Error: Received status code " . $status_code;
|
| 483 |
+
|
| 484 |
+
$decoded = json_decode($body, true);
|
| 485 |
+
if ($decoded && isset($decoded['error'])) {
|
| 486 |
+
$error_message .= " - " . $decoded['error'];
|
| 487 |
+
}
|
| 488 |
+
|
| 489 |
+
return array(
|
| 490 |
+
'success' => false,
|
| 491 |
+
'error' => $error_message,
|
| 492 |
+
'result' => 'Error: ' . $error_message
|
| 493 |
+
);
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
$decoded = json_decode($body, true);
|
| 497 |
+
|
| 498 |
+
if (isset($decoded['generated_text'])) {
|
| 499 |
+
return array(
|
| 500 |
+
'success' => true,
|
| 501 |
+
'result' => $decoded['generated_text']
|
| 502 |
+
);
|
| 503 |
+
} else {
|
| 504 |
+
return array(
|
| 505 |
+
'success' => false,
|
| 506 |
+
'error' => 'Unexpected response format',
|
| 507 |
+
'result' => 'Error: Could not parse the AI response.'
|
| 508 |
+
);
|
| 509 |
+
}
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
/**
|
| 513 |
+
* Generate test analysis results (used when test mode is enabled)
|
| 514 |
+
*/
|
| 515 |
+
function phi_shampoo_generate_test_analysis($data) {
|
| 516 |
+
$ingredients = $data['ingredients'];
|
| 517 |
+
$has_allergies = !empty($data['allergies']);
|
| 518 |
+
|
| 519 |
+
if (strpos(strtolower($ingredients), 'sulfate') !== false ||
|
| 520 |
+
strpos(strtolower($ingredients), 'methylisothiazolinone') !== false) {
|
| 521 |
+
|
| 522 |
+
$result = "NOT RECOMMENDED\n\n";
|
| 523 |
+
$result .= "Based on the ingredients analysis, this shampoo contains some potentially problematic ingredients for your specific health profile.\n\n";
|
| 524 |
+
|
| 525 |
+
if (strpos(strtolower($ingredients), 'sodium laureth sulfate') !== false ||
|
| 526 |
+
strpos(strtolower($ingredients), 'sodium lauryl sulfate') !== false) {
|
| 527 |
+
$result .= "• Sodium Laureth/Lauryl Sulfate: A strong detergent that can be irritating, especially for sensitive skin or those with certain allergies.\n";
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
if (strpos(strtolower($ingredients), 'methylisothiazolinone') !== false) {
|
| 531 |
+
$result .= "• Methylisothiazolinone: A preservative that is a known allergen and has been associated with skin sensitization.\n";
|
| 532 |
+
}
|
| 533 |
+
|
| 534 |
+
if ($has_allergies) {
|
| 535 |
+
$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";
|
| 536 |
+
}
|
| 537 |
+
|
| 538 |
+
$result .= "\nRecommendation: Look for shampoos with gentler cleansing agents like Cocamidopropyl Hydroxysultaine, Sodium Cocoyl Isethionate or Coco Glucoside.";
|
| 539 |
+
} else {
|
| 540 |
+
$result = "SAFE TO USE\n\n";
|
| 541 |
+
$result .= "Based on the ingredients analysis, this shampoo appears to be safe for general use. The formula contains:\n\n";
|
| 542 |
+
$result .= "• Mild cleansing agents that effectively clean without excessive drying\n";
|
| 543 |
+
$result .= "• Conditioning agents to help maintain hair moisture\n";
|
| 544 |
+
|
| 545 |
+
if (strpos(strtolower($ingredients), 'fragrance') !== false ||
|
| 546 |
+
strpos(strtolower($ingredients), 'parfum') !== false) {
|
| 547 |
+
$result .= "• Fragrances are present, which could potentially cause sensitivity in some individuals\n";
|
| 548 |
+
}
|
| 549 |
+
|
| 550 |
+
$result .= "\nNo major red flags were identified in this formulation that would likely trigger adverse reactions.";
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
return array(
|
| 554 |
+
'success' => true,
|
| 555 |
+
'result' => $result
|
| 556 |
+
);
|
| 557 |
+
}
|
| 558 |
+
|
| 559 |
+
/**
|
| 560 |
+
* Get the Hugging Face API token
|
| 561 |
+
* Returns the API token from WordPress options or an empty string if not set
|
| 562 |
+
*/
|
| 563 |
+
function phi_shampoo_get_api_token() {
|
| 564 |
+
$token = get_option('phi_shampoo_hf_api_token', '');
|
| 565 |
+
|
| 566 |
+
// For extra security, you can decrypt the token here if you stored it encrypted
|
| 567 |
+
return $token;
|
| 568 |
+
}
|
| 569 |
+
|
| 570 |
+
/**
|
| 571 |
+
* Admin menu
|
| 572 |
+
*/
|
| 573 |
+
function phi_shampoo_admin_menu() {
|
| 574 |
+
add_menu_page(
|
| 575 |
+
'Phi Shampoo Analyzer',
|
| 576 |
+
'Phi Shampoo',
|
| 577 |
+
'manage_options',
|
| 578 |
+
'phi-shampoo-settings',
|
| 579 |
+
'phi_shampoo_settings_page',
|
| 580 |
+
'dashicons-admin-generic'
|
| 581 |
+
);
|
| 582 |
+
}
|
| 583 |
+
add_action('admin_menu', 'phi_shampoo_admin_menu');
|
| 584 |
+
|
| 585 |
+
/**
|
| 586 |
+
* Admin settings page
|
| 587 |
+
*/
|
| 588 |
+
function phi_shampoo_settings_page() {
|
| 589 |
+
// Save settings if submitted
|
| 590 |
+
if (isset($_POST['phi_shampoo_save_settings']) && isset($_POST['phi_shampoo_settings_nonce'])) {
|
| 591 |
+
if (wp_verify_nonce($_POST['phi_shampoo_settings_nonce'], 'phi_shampoo_settings')) {
|
| 592 |
+
|
| 593 |
+
// Sanitize and save the API token
|
| 594 |
+
if (isset($_POST['phi_shampoo_hf_api_token'])) {
|
| 595 |
+
$api_token = sanitize_text_field($_POST['phi_shampoo_hf_api_token']);
|
| 596 |
+
update_option('phi_shampoo_hf_api_token', $api_token);
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
// Save test mode setting
|
| 600 |
+
$test_mode = isset($_POST['phi_shampoo_test_mode']) ? true : false;
|
| 601 |
+
update_option('phi_shampoo_test_mode', $test_mode);
|
| 602 |
+
|
| 603 |
+
echo '<div class="notice notice-success is-dismissible"><p>Settings saved successfully!</p></div>';
|
| 604 |
+
} else {
|
| 605 |
+
echo '<div class="notice notice-error is-dismissible"><p>Security check failed. Settings not saved.</p></div>';
|
| 606 |
+
}
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
// Get current settings
|
| 610 |
+
$api_token = get_option('phi_shampoo_hf_api_token', '');
|
| 611 |
+
$test_mode = get_option('phi_shampoo_test_mode', true);
|
| 612 |
+
|
| 613 |
+
?>
|
| 614 |
+
<div class="wrap">
|
| 615 |
+
<h1>Phi Shampoo Analyzer Settings</h1>
|
| 616 |
+
|
| 617 |
+
<form method="post" action="">
|
| 618 |
+
<?php wp_nonce_field('phi_shampoo_settings', 'phi_shampoo_settings_nonce'); ?>
|
| 619 |
+
|
| 620 |
+
<table class="form-table">
|
| 621 |
+
<tr>
|
| 622 |
+
<th scope="row">Hugging Face API Token</th>
|
| 623 |
+
<td>
|
| 624 |
+
<input type="password" name="phi_shampoo_hf_api_token" value="<?php echo esc_attr($api_token); ?>" class="regular-text" />
|
| 625 |
+
<p class="description">
|
| 626 |
+
Enter your Hugging Face API token.
|
| 627 |
+
<a href="https://huggingface.co/settings/tokens" target="_blank">Get a token here</a>.
|
| 628 |
+
</p>
|
| 629 |
+
</td>
|
| 630 |
+
</tr>
|
| 631 |
+
<tr>
|
| 632 |
+
<th scope="row">Test Mode</th>
|
| 633 |
+
<td>
|
| 634 |
+
<label>
|
| 635 |
+
<input type="checkbox" name="phi_shampoo_test_mode" <?php checked($test_mode, true); ?> />
|
| 636 |
+
Enable test mode (uses pre-defined responses instead of API calls)
|
| 637 |
+
</label>
|
| 638 |
+
</td>
|
| 639 |
+
</tr>
|
| 640 |
+
</table>
|
| 641 |
+
|
| 642 |
+
<div class="phi-shampoo-settings-info">
|
| 643 |
+
<h2>Important Information</h2>
|
| 644 |
+
<p>To use this plugin, make sure:</p>
|
| 645 |
+
<ul>
|
| 646 |
+
<li>You have a valid Hugging Face account</li>
|
| 647 |
+
<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>
|
| 648 |
+
<li>Your API token has the necessary permissions (at least "Read" access)</li>
|
| 649 |
+
</ul>
|
| 650 |
+
</div>
|
| 651 |
+
|
| 652 |
+
<p class="submit">
|
| 653 |
+
<input type="submit" name="phi_shampoo_save_settings" class="button-primary" value="Save Settings" />
|
| 654 |
+
</p>
|
| 655 |
+
</form>
|
| 656 |
+
</div>
|
| 657 |
+
<?php
|
| 658 |
+
}
|