Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions assets/js/ai-form-builder.min.js

Large diffs are not rendered by default.

21 changes: 14 additions & 7 deletions assets/js/components/FormSuccessStage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@
<input type="radio" :name="`field_${field.id}`" :value="option.value" disabled class="wpuf-radio-input wpuf-text-emerald-600">
<label class="wpuf-radio-label wpuf-text-base wpuf-leading-6 wpuf-text-gray-700">{{ option.label }}</label>
</div>
<span class="wpuf-text-base wpuf-font-medium wpuf-text-emerald-600">{{ field.currency_symbol || '$' }}{{ field.prices && field.prices[option.value] ? field.prices[option.value] : '0' }}</span>
<span class="wpuf-text-base wpuf-font-medium wpuf-text-emerald-600">{{ field.prices && field.prices[option.value] ? field.prices[option.value] : '0' }}</span>
</div>
</template>
<div v-else class="wpuf-text-gray-400 wpuf-text-base wpuf-leading-6">{{ __('No pricing options configured', 'wp-user-frontend') }}</div>
Expand All @@ -298,31 +298,38 @@
<input type="checkbox" :value="option.value" disabled class="wpuf-checkbox-input wpuf-text-emerald-600">
<label class="wpuf-checkbox-label wpuf-text-base wpuf-leading-6 wpuf-text-gray-700">{{ option.label }}</label>
</div>
<span class="wpuf-text-base wpuf-font-medium wpuf-text-emerald-600">{{ field.currency_symbol || '$' }}{{ field.prices && field.prices[option.value] ? field.prices[option.value] : '0' }}</span>
<span class="wpuf-text-base wpuf-font-medium wpuf-text-emerald-600">{{ field.prices && field.prices[option.value] ? field.prices[option.value] : '0' }}</span>
</div>
</template>
<div v-else class="wpuf-text-gray-400 wpuf-text-base wpuf-leading-6">{{ __('No pricing options configured', 'wp-user-frontend') }}</div>
</div>
</div>

<!-- WPUF Pricing Dropdown -->
<div v-else-if="getWPUFFieldType(field) === 'pricing_dropdown'" class="wpuf-form-pricing-dropdown-container">
<select disabled class="wpuf-form-input wpuf-border wpuf-border-[#E3E5E8] wpuf-rounded-[10px] wpuf-p-3 wpuf-text-base wpuf-leading-6 wpuf-bg-white wpuf-w-full">
<div v-else-if="getWPUFFieldType(field) === 'pricing_dropdown'" class="wpuf-form-pricing-dropdown-container wpuf-relative">
<select
disabled
class="wpuf-form-select wpuf-form-input wpuf-border wpuf-border-[#E3E5E8] wpuf-rounded-[10px] wpuf-p-3 wpuf-text-base wpuf-leading-6 wpuf-bg-white wpuf-w-full wpuf-text-gray-700 wpuf-cursor-pointer focus:wpuf-outline-none focus:wpuf-ring-2 focus:wpuf-ring-emerald-500 focus:wpuf-border-emerald-500 wpuf-pr-10"
>
<option value="" class="wpuf-text-gray-400">{{ field.first || __('- Select -', 'wp-user-frontend') }}</option>
<template v-if="field.options && normalizeOptions(field.options).length > 0">
<option v-for="option in normalizeOptions(field.options)" :key="option.value" :value="option.value">
{{ option.label }} - {{ field.currency_symbol || '$' }}{{ field.prices && field.prices[option.value] ? field.prices[option.value] : '0' }}
{{ option.label }} - {{ field.prices && field.prices[option.value] ? field.prices[option.value] : '0' }}
</option>
</template>
<option v-else disabled class="wpuf-text-gray-400">{{ __('No pricing options configured', 'wp-user-frontend') }}</option>
</select>
<svg class="wpuf-select-caret wpuf-absolute wpuf-right-3 wpuf-top-1/2 wpuf-transform wpuf--translate-y-1/2 wpuf-pointer-events-none wpuf-text-gray-500" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 7.5L10 12.5L15 7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>

<!-- WPUF Pricing Multiselect -->
<div v-else-if="getWPUFFieldType(field) === 'pricing_multiselect'" class="wpuf-form-pricing-multiselect-container wpuf-relative">
<select multiple disabled class="wpuf-form-multiselect wpuf-border wpuf-border-[#E3E5E8] wpuf-rounded-[10px] wpuf-p-3 wpuf-text-base wpuf-leading-6 wpuf-bg-white wpuf-w-full" style="min-height: 120px;">
<template v-if="field.options && normalizeOptions(field.options).length > 0">
<option v-for="option in normalizeOptions(field.options)" :key="option.value" :value="option.value" class="wpuf-py-2 wpuf-px-3">
{{ option.label }} - {{ field.currency_symbol || '$' }}{{ field.prices && field.prices[option.value] ? field.prices[option.value] : '0' }}
{{ option.label }} - {{ field.prices && field.prices[option.value] ? field.prices[option.value] : '0' }}
</option>
</template>
<option v-else disabled class="wpuf-text-gray-400">{{ __('No pricing options configured', 'wp-user-frontend') }}</option>
Expand All @@ -333,7 +340,7 @@
<div v-else-if="getWPUFFieldType(field) === 'cart_total'" class="wpuf-form-cart-total wpuf-border-2 wpuf-border-emerald-200 wpuf-bg-emerald-50 wpuf-rounded-[10px] wpuf-p-4">
<div class="wpuf-flex wpuf-items-center wpuf-justify-between">
<span class="wpuf-text-lg wpuf-font-semibold wpuf-text-gray-800">{{ field.label || __('Total', 'wp-user-frontend') }}</span>
<span class="wpuf-text-2xl wpuf-font-bold wpuf-text-emerald-600">{{ field.currency_symbol || '$' }}0.00</span>
<span class="wpuf-text-2xl wpuf-font-bold wpuf-text-emerald-600">0.00</span>
</div>
</div>

Expand Down
6 changes: 0 additions & 6 deletions includes/AI/RestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,26 @@
*/
public function register_routes() {
// Generate form endpoint
register_rest_route($this->namespace, '/' . $this->rest_base . '/generate', [

Check failure on line 73 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Opening parenthesis of a multi-line function call must be the last content on the line
'methods' => WP_REST_Server::CREATABLE,
'callback' => [$this, 'generate_form'],

Check failure on line 75 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Expected 1 space before the array closer in a single line array. Found: no spaces

Check failure on line 75 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Expected 1 space after the array opener in a single line array. Found: no spaces
'permission_callback' => [$this, 'check_permission'],

Check failure on line 76 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Expected 1 space before the array closer in a single line array. Found: no spaces

Check failure on line 76 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Expected 1 space after the array opener in a single line array. Found: no spaces
'args' => [
'prompt' => [
'required' => true,
'type' => 'string',
'sanitize_callback' => 'sanitize_textarea_field',
'validate_callback' => [$this, 'validate_prompt']

Check failure on line 82 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Expected 1 space before the array closer in a single line array. Found: no spaces

Check failure on line 82 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

There should be a comma after the last array item in a multi-line array.

Check failure on line 82 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Expected 1 space after the array opener in a single line array. Found: no spaces
],
'session_id' => [
'required' => false,
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field'

Check failure on line 87 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

There should be a comma after the last array item in a multi-line array.
],
'conversation_context' => [
'required' => false,
'type' => 'object',
'default' => []

Check failure on line 92 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

There should be a comma after the last array item in a multi-line array.
],
'form_type' => [
'required' => false,
Expand Down Expand Up @@ -508,7 +508,7 @@
}

// Check if user can create forms
if (!current_user_can('edit_posts') && !current_user_can('wpuf_create_forms')) {

Check warning on line 511 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Found unknown capability "wpuf_create_forms" in function call to current_user_can(). Please check the spelling of the capability. If this is a custom capability, please verify the capability is registered with WordPress via a call to WP_Role(s)->add_cap(). Custom capabilities can be made known to this sniff by setting the "custom_capabilities" property in the PHPCS ruleset.
return false;
}

Expand Down Expand Up @@ -643,7 +643,7 @@
'post_status' => 'publish',
'post_parent' => $form_id,
'menu_order' => $order,
'post_content' => serialize($field) // WPUF stores field data as serialized content

Check warning on line 646 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

serialize() found. Serialized data has known vulnerability problems with Object Injection. JSON is generally a better approach for serializing data. See https://www.owasp.org/index.php/PHP_Object_Injection
);

$field_id = wp_insert_post($field_post);
Expand Down Expand Up @@ -1042,7 +1042,7 @@

if ( $needs_conversion ) {
// Extract custom properties (everything except template, label, and id)
$custom_props = array_diff_key( $field, [ 'template' => '', 'label' => '', 'id' => '' ] );

Check warning on line 1045 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

When a multi-item array uses associative keys, each value should start on a new line.

// Build complete field structure using Field_Templates
// This ensures all required properties (zoom, default_pos, wpuf_cond, etc.) are added
Expand Down Expand Up @@ -1182,7 +1182,7 @@
wp_update_post([
'ID' => $existing_posts[$order]->ID,
'menu_order' => $order,
'post_content' => serialize($field)

Check warning on line 1185 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

serialize() found. Serialized data has known vulnerability problems with Object Injection. JSON is generally a better approach for serializing data. See https://www.owasp.org/index.php/PHP_Object_Injection
]);
} else {
// Create new post
Expand All @@ -1191,7 +1191,7 @@
'post_status' => 'publish',
'post_parent' => $form_id,
'menu_order' => $order,
'post_content' => serialize($field)

Check warning on line 1194 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

serialize() found. Serialized data has known vulnerability problems with Object Injection. JSON is generally a better approach for serializing data. See https://www.owasp.org/index.php/PHP_Object_Injection
);
wp_insert_post($field_post);
}
Expand Down Expand Up @@ -1302,7 +1302,7 @@
}
if (isset($field['css'])) {
// More restrictive CSS sanitization to prevent XSS
$field['css'] = strip_tags($field['css']);

Check warning on line 1305 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

strip_tags() is discouraged. Use the more comprehensive wp_strip_all_tags() instead.
$field['css'] = preg_replace('/[^a-zA-Z0-9\s\-_\.\#\:\;\,\%\(\)]/', '', $field['css']);
$field['css'] = substr($field['css'], 0, 200); // Limit length
}
Expand Down Expand Up @@ -1662,7 +1662,7 @@
$field['columns'] = '2';
}
if (!isset($field['inner_fields']) || empty($field['inner_fields'])) {
$field['inner_fields'] = [ 'column-1' => [], 'column-2' => [] ];

Check warning on line 1665 in includes/AI/RestController.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

When a multi-item array uses associative keys, each value should start on a new line.
}
}

Expand All @@ -1683,9 +1683,6 @@
'third_item' => '50',
];
}
if (!isset($field['currency_symbol'])) {
$field['currency_symbol'] = '$';
}
if (!isset($field['enable_quantity'])) {
$field['enable_quantity'] = 'no';
}
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of currency_symbol default assignment in RestController.php appears inconsistent with the field templates in Field_Templates.php (lines 1434, 1467, 1497, 1529, 1561, 1582), which still define currency_symbol defaults. This creates a mismatch where template definitions include the property but the sanitization layer no longer sets it. Consider also removing currency_symbol from the field template definitions in includes/AI/Field_Templates.php or documenting why templates retain it while sanitization doesn't set it.

Suggested change
}
}
if (!isset($field['currency_symbol'])) {
$field['currency_symbol'] = '$';
}

Copilot uses AI. Check for mistakes.
Expand All @@ -1711,9 +1708,6 @@
if (!isset($field['show_summary'])) {
$field['show_summary'] = 'yes';
}
if (!isset($field['currency_symbol'])) {
$field['currency_symbol'] = 'USD';
}
}
}

Expand Down
134 changes: 11 additions & 123 deletions includes/AI/wpuf-ai-minimal-prompt-registration.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,6 @@ When user requests a registration form or modifications:
- `repeat_field` - Repeatable field group
- `toc` - Terms and conditions agreement

### Pricing Fields (Pro):
- `price_field` - Price input field (allows user to enter custom amount)
- `pricing_radio` - Radio button pricing options
- `pricing_checkbox` - Checkbox pricing options (multiple selections)
- `pricing_dropdown` - Dropdown pricing options
- `pricing_multiselect` - Multi-select pricing options
- `cart_total` - Cart total display (REQUIRED when using any pricing field)

## RESPONSE FORMAT:

Expand Down Expand Up @@ -408,123 +401,17 @@ Examples with meaningful options:
### Address Field:
- Automatically includes: street, city, state, zip, country

### Pricing Fields:
⚠️ CRITICAL: When you include ANY pricing field (price_field, pricing_radio, pricing_checkbox, pricing_dropdown, pricing_multiselect), you MUST also include a `cart_total` field at the end of your fields array.

⚠️ IMPORTANT: When adding pricing fields:
- If user specifies prices, use those exact values
- If user does NOT specify prices, generate realistic demo prices based on the field label and context
- NEVER leave prices empty or set to "0"
- Always provide 3-5 pricing options with appropriate demo amounts
- For cart_total field, do NOT include prices (it calculates automatically)

**Currency Symbol:**
You MUST ONLY use one of these valid currency symbols. Do NOT use any other symbol:
- $ (USD, AUD, CAD, HKD, MXN, NZD, SGD, AZD, CLP, COP)
- € (EUR)
- £ (GBP)
- ¥ (CNY, JPY)
- ₹ (INR, MUR, NPR)
- د.إ (AED)
- ৳ (BDT)
- R$ (BRL)
- лв. (BGN)
- Kč (CZK)
- kr. (DKK, NOK, SEK, ISK)
- RD$ (DOP)
- DA; (DZD)
- Kn (HRK)
- Ft (HUF)
- Rp (IDR)
- Rs (PKR, INR)
- Rs. (NPR)
- ₪ (ILS)
- ₭ (KIP)
- ₩ (KRW)
- RM (MYR)
- ₦ (NGN)
- N$ (NAD)
- ر.ع. (OMR)
- ﷼ (IRR)
- ₲ (PYG)
- ₱ (PHP)
- zł (PLN)
- lei (RON)
- руб. (RUB)
- SR (SR)
- R (ZAR)
- CHF (CHF)
- NT$ (TWD)
- ฿ (THB)
- ₺ (TRY)
- TT$ (TTD)
- ₫ (VND)
- EGP (EGP)
- د.أ (JOD)

**Price Field (User Input):**
For user-entered custom amounts, use `price_field`:
```json
{
"template": "price_field",
"label": "Donation Amount",
"currency_symbol": "$",
"price_input_mode": "user_input",
"price_min": "1",
"price_max": "10000",
"placeholder": "Enter your donation amount"
}
```

**Price Field (Fixed Amount):**
For fixed price that users cannot change:
```json
{
"template": "price_field",
"label": "Registration Fee",
"currency_symbol": "$",
"price_input_mode": "fixed",
"default": "25"
}
```

Price Field Properties:
- `price_input_mode` - "user_input" (allows custom amount) or "fixed" (read-only default)
- `price_min` - Minimum allowed value (only for user_input mode)
- `price_max` - Maximum allowed value (only for user_input mode)
- `price_hidden` - "yes" or "no" - Hide field on frontend (still used in calculations)
- `default` - Default/fixed price value
- `placeholder` - Placeholder text
- `currency_symbol` - Currency symbol (use valid symbols from list below)
## ⚠️ PRICING FIELDS ARE NOT ALLOWED IN REGISTRATION FORMS:
**CRITICAL:** Pricing fields are ONLY for post forms, NOT for registration forms.
- DO NOT use `price_field`
- DO NOT use `pricing_radio`
- DO NOT use `pricing_checkbox`
- DO NOT use `pricing_dropdown`
- DO NOT use `pricing_multiselect`
- DO NOT use `cart_total`

**Pricing Selection Fields:**
Include `options`, `prices`, and `currency_symbol` for pricing fields:
```json
{
"template": "pricing_radio",
"label": "Membership Plan",
"options": {
"free": "Free Member",
"basic": "Basic Member",
"premium": "Premium Member"
},
"prices": {
"free": "0",
"basic": "10",
"premium": "25"
},
"currency_symbol": "$"
}
```

**IMPORTANT:** Always add cart_total field when pricing fields are present:
```json
{
"template": "cart_total",
"label": "Total Amount",
"currency_symbol": "$"
}
```
If a user requests pricing/payment functionality in a registration form:
- Inform them: "Pricing fields are only available for post forms, not registration forms. For membership subscriptions or paid registrations, please use a dedicated membership plugin alongside WPUF."

## MODIFICATION RULES:
When user says "add X field":
Expand Down Expand Up @@ -646,4 +533,5 @@ When user says "make X field radio button" or "change X to dropdown":
3. Use appropriate registration field templates
4. Set sensible defaults for required fields
5. ONLY registration/profile fields - NO post fields (post_title, post_content, featured_image, etc.)
6. NO pricing fields (price_field, pricing_radio, pricing_checkbox, pricing_dropdown, pricing_multiselect, cart_total) - these are ONLY for post forms

70 changes: 9 additions & 61 deletions includes/AI/wpuf-ai-minimal-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,51 +482,7 @@ Note: Always use 3 as the maximum, never exceed it.
- Always provide 3-5 pricing options with appropriate demo amounts
- For cart_total field, do NOT include prices (it calculates automatically)

**Currency Symbol:**
You MUST ONLY use one of these valid currency symbols. Do NOT use any other symbol:
- $ (USD, AUD, CAD, HKD, MXN, NZD, SGD, AZD, CLP, COP)
- € (EUR)
- £ (GBP)
- ¥ (CNY, JPY)
- ₹ (INR, MUR, NPR)
- د.إ (AED)
- ৳ (BDT)
- R$ (BRL)
- лв. (BGN)
- Kč (CZK)
- kr. (DKK, NOK, SEK, ISK)
- RD$ (DOP)
- DA; (DZD)
- Kn (HRK)
- Ft (HUF)
- Rp (IDR)
- Rs (PKR, INR)
- Rs. (NPR)
- ₪ (ILS)
- ₭ (KIP)
- ₩ (KRW)
- RM (MYR)
- ₦ (NGN)
- N$ (NAD)
- ر.ع. (OMR)
- ﷼ (IRR)
- ₲ (PYG)
- ₱ (PHP)
- zł (PLN)
- lei (RON)
- руб. (RUB)
- SR (SR)
- R (ZAR)
- CHF (CHF)
- NT$ (TWD)
- ฿ (THB)
- ₺ (TRY)
- TT$ (TTD)
- ₫ (VND)
- EGP (EGP)
- د.أ (JOD)

Include `options`, `prices`, and `currency_symbol` for pricing fields:
Include `options` and `prices` for pricing fields:
```json
{
"template": "pricing_radio",
Expand All @@ -540,17 +496,15 @@ Include `options`, `prices`, and `currency_symbol` for pricing fields:
"basic": "10",
"premium": "25",
"enterprise": "50"
},
"currency_symbol": "$"
}
}
```

**IMPORTANT:** Always add cart_total field when pricing fields are present:
```json
{
"template": "cart_total",
"label": "Total Amount",
"currency_symbol": "$"
"label": "Total Amount"
}
```

Expand All @@ -569,13 +523,11 @@ User: "add membership pricing with monthly $10 and yearly $100"
"prices": {
"monthly": "10",
"yearly": "100"
},
"currency_symbol": "$"
}
},
{
"template": "cart_total",
"label": "Total",
"currency_symbol": "$"
"label": "Total"
}
]
}
Expand All @@ -598,13 +550,11 @@ User: "add ticket pricing field"
"general": "50",
"vip": "150",
"backstage": "300"
},
"currency_symbol": "$"
}
},
{
"template": "cart_total",
"label": "Total Amount",
"currency_symbol": "$"
"label": "Total Amount"
}
]
}
Expand All @@ -630,13 +580,11 @@ User: "add add-ons pricing"
"gift_wrap": "5",
"insurance": "10",
"premium_support": "25"
},
"currency_symbol": "$"
}
},
{
"template": "cart_total",
"label": "Total",
"currency_symbol": "$"
"label": "Total"
}
]
}
Expand Down
Loading