-
Notifications
You must be signed in to change notification settings - Fork 151
Feat/wpuf ai form builder design #1697
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/wpuf ai form builder design #1697
Conversation
Introduces a comprehensive Markdown reference for all available WPUF (WordPress User Frontend) post form fields, including field types, properties, options, Pro vs Free distinctions, validation rules, templates, and usage guidelines for AI-driven form creation
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes. π€ Files selected but had no reviewable changes (1)
β Files ignored due to path filters (1)
You can disable this status message by setting the WalkthroughAdds a complete AI Form Builder feature: backend AI orchestration (multi-provider FormGenerator, PredefinedProvider), REST API, admin handlers/templates, Vue frontend with components and build pipeline updates, new assets/styles, settings UI, diagnostic/test scripts, and plugin bootstrap wiring. Changes
Estimated code review effortπ― 5 (Critical) | β±οΈ ~120+ minutes Possibly related PRs
Suggested reviewers
Pre-merge checks (2 passed, 1 inconclusive)β Failed checks (1 inconclusive)
β Passed checks (2 passed)
Poem
β¨ Finishing Touchesπ§ͺ Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 49
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (1)
includes/Admin/Posting.php (1)
156-163: Register or guard localization of βwpuf-field-initializationβ
The handle βwpuf-field-initializationβ isnβt registered in includes/Assets.php, so calling wp_localize_script() on it will enqueue data that never prints. Either add a corresponding entry to get_scripts() (so βwpuf-field-initializationβ is registered) or wrap the wp_localize_script() call in a check, for example:if ( wp_script_is( 'wpuf-field-initialization', 'registered' ) ) { wp_localize_script( 'wpuf-field-initialization', 'wpuf_field_initializer', [ 'asset_uri' => defined( 'WPUF_PRO_ASSET_URI' ) ? WPUF_PRO_ASSET_URI : '', ] ); }Location: includes/Admin/Posting.php:157β160
π§Ή Nitpick comments (58)
tailwind.config.js (1)
12-13: LGTM β broadened content globs ensure PHP template classes are scanned.Consider narrowing if build times spike (e.g., target only includes/Admin/template-parts and other known template dirs) or use a safelist for dynamic classes.
src/css/ai-form-builder.css (1)
13-16: LGTM β scoped spin animation with wpuf- prefix.Consider honoring reduced motion:
@media (prefers-reduced-motion: reduce) { .wpuf-animate-spin { animation: none; } }setup-google-ai.php (1)
7-14: Support env override to avoid editing the file.Allow running via
WPUF_GOOGLE_API_KEY=... wp eval-file ....-// REPLACE THIS WITH YOUR ACTUAL API KEY -$api_key = 'YOUR_GOOGLE_API_KEY_HERE'; +// Prefer environment variable if present +$api_key = getenv('WPUF_GOOGLE_API_KEY') ?: 'YOUR_GOOGLE_API_KEY_HERE';test-google-gemini.php (1)
101-114: Improve error reporting for non-200 responses.Surface cURL errors and HTTP reason text to speed up debugging.
} else { - echo "β ERROR: Request failed\n"; - $error_data = json_decode($response, true); + echo "β ERROR: Request failed\n"; + $curl_err = isset($ch) ? '' : ''; + $error_data = json_decode($response, true); if (isset($error_data['error'])) { echo "Error: " . $error_data['error']['message'] . "\n"; echo "Status: " . $error_data['error']['status'] . "\n"; if (strpos($error_data['error']['message'], 'API key not valid') !== false) { echo "\nβ οΈ Please check your API key!\n"; echo "Get one at: https://aistudio.google.com/\n"; } } else { - echo "Response: $response\n"; + echo "Response body: $response\n"; } }Additionally, check and print
curl_error($ch)when$response === false.includes/functions/settings-options.php (1)
655-685: Model list: prefer stable βlatestβ aliases and verify defaults.Several IDs (e.g., gpt-3.5-turbo, older dated Anthropic models) may be deprecated or superseded. Consider using each providerβs βlatestβ aliases and setting a sensible default (e.g., gpt-4o-mini or gemini-1.5-flash) to reduce support churn.
Would you like me to switch to βlatestβ aliases and gate model options by selected provider (to avoid cross-provider IDs in the dropdown)?
assets/css/admin/form-builder.css (4)
1209-1211: Duplicate transition declarations in same rule.Two
transitiondeclarations override each other. Merge into one to satisfy linters.- transition: visibility 0.2s; - transition: padding 0.2s ease-out, - background-color 0.2s ease-out; + transition: visibility 0.2s, padding 0.2s ease-out, background-color 0.2s ease-out;Note: If this file is generated, fix in the source (Tailwind/CSS) or suppress the linter for generated assets.
3899-3901:visibility: collapseon non-table elements is inconsistent across browsers.Use
display: noneorvisibility: hiddendepending on layout needs.-.wpuf-collapse { - visibility: collapse; -} +.wpuf-collapse { visibility: hidden; } /* or display: none; if layout permits */
7075-7113: Hard-coded green color for range slider; use theme tokens.Replace raw
greenwith your design tokens to support theming (light/dark/brand).-input[type="range"] { - ... - background: green; - ... -} +input[type="range"] { + ... + background: var(--fallback-su, oklch(var(--su)/1)); + ... +} -input[type="range"]::-webkit-slider-thumb { - background: green; -} +input[type="range"]::-webkit-slider-thumb { + background: var(--fallback-su, oklch(var(--su)/1)); +} -input[type="range"]::-moz-range-thumb { - background: green; -} +input[type="range"]::-moz-range-thumb { + background: var(--fallback-su, oklch(var(--su)/1)); +}
2661-2786: Collapse control: multiple duplicate transition timing declarations.Several
transition-timing-functionlines repeat; keep one to reduce CSS size and appease linters.- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);If compiled by Tailwind/DaisyUI, consider post-processing with cssnano to dedupe in dist files.
Lib/AI/PredefinedProvider.php (4)
818-821: Donβt overwrite response['fields'] with WPUF fieldsOverwriting the generic
fieldswith WPUF-shaped fields can break consumers expecting the original shape. Keepfieldsas-is and expose onlywpuf_fields(optionally includeoriginal_fieldsfor debugging).Apply this diff to stop overriding
fields:- // Update the response fields to show the converted WPUF fields in debug logs - // This ensures RestController logs show the correct field structure - $response['fields'] = $wpuf_fields; + // Keep original `fields` intact; `wpuf_fields` already contains the converted structure.If you still want debug visibility, capture the original before conversion and add
original_fields:// place before conversion $original_fields = $response['fields']; // after conversion $response['original_fields'] = $original_fields;
1023-1040: Taxonomy field may be missing required props and has mixed boolean conventionWPUF taxonomy fields typically need the taxonomy slug (often via
nameor a dedicatedtaxonomykey) and toggle values as 'yes'/'no'. Hereshow_inlineis set tofalse(boolean), while other flags use 'no'. Also consider explicitly setting the taxonomy to 'category'.Proposed adjustments:
- $wpuf_field['show_inline'] = false; + $wpuf_field['show_inline'] = 'no'; + // Ensure taxonomy slug is explicit if WPUF expects it + $wpuf_field['taxonomy'] = 'category';Please verify against WPUFβs taxonomy field schema to confirm the expected keys.
1134-1187: Prefix WP global functions for clarity and slight perf (FQN) + ensure availabilityIn namespaced PHP, using global WP functions via FQNs is clearer and avoids an extra global lookup. Apply to
__(),get_option(),current_time(), etc.Apply representative changes:
- 'message_restrict' => __('This page is restricted. Please {login} / {register} to view this page.', 'wp-user-frontend'), + 'message_restrict' => \__('This page is restricted. Please {login} / {register} to view this page.', 'wp-user-frontend'), - 'submit_text' => sprintf(__('Create %s', 'wp-user-frontend'), $title), + 'submit_text' => \sprintf(\__('Create %s', 'wp-user-frontend'), $title), - 'new_to' => get_option('admin_email', ''), - 'new_subject' => sprintf(__('New %s has been created', 'wp-user-frontend'), strtolower($title)), + 'new_to' => \get_option('admin_email', ''), + 'new_subject' => \sprintf(\__('New %s has been created', 'wp-user-frontend'), \strtolower($title)),Also update earlier
current_time('mysql')similarly:- $response['generated_at'] = current_time('mysql'); + $response['generated_at'] = \current_time('mysql');
949-981: Map consistency between input_type and template; avoid later overridesSome types (e.g., 'number', 'file', 'image') are remapped again in
addFieldSpecificProperties, contradicting the βdonβt overrideβ comment. Keep mapping in one place (here) and makeaddFieldSpecificPropertiesadditive only.Simplify
addFieldSpecificPropertiesby removinginput_type/templatereassignments and centralize the mapping here to reduce drift.assets/js/components/FormProcessingStage.vue (6)
125-125: Use WordPress JS i18n when available and support domain argumentRely on
wp.i18n.__if present and pass through extra args. Current method ignores the text-domain parameter.Apply:
- __: window.__ || ((text) => text), + __(...args) { + const fn = window?.wp?.i18n?.__ || ((t) => t); + return fn(...args); + },
136-152: Clear timers on unmount to prevent leaks and stray emitsTrack
setTimeoutIDs and clear them onbeforeUnmount. This component schedules multiple timers and currently never clears them.Apply:
- steps.forEach((step, index) => { - setTimeout(() => { + this._timers = this._timers || []; + steps.forEach((step, index) => { + const id = setTimeout(() => { this.currentStep = step; if (step === 4) { - setTimeout(() => { + const cid = setTimeout(() => { this.showConfetti = true; this.confettiUrl = this.getConfettiUrl(); - setTimeout(() => { + const fid = setTimeout(() => { this.$emit('generation-complete'); this.isProcessing = false; - }, this.completeDelay); - }, this.confettiDelay); + }, this.completeDelay); + this._timers.push(fid); + }, this.confettiDelay); + this._timers.push(cid); } - }, (index + 1) * this.stepDelay); + }, (index + 1) * this.stepDelay); + this._timers.push(id); });Add this hook near the end of the script:
beforeUnmount() { (this._timers || []).forEach(clearTimeout); this._timers = []; }
2-3: Improve accessibility: announce progress and busy stateAdd ARIA to help screen readers track progress.
Apply:
- <div class="wpuf-ai-form-wrapper wpuf-font-sans wpuf-bg-white wpuf-w-full wpuf-h-screen wpuf-overflow-hidden wpuf-relative"> + <div class="wpuf-ai-form-wrapper wpuf-font-sans wpuf-bg-white wpuf-w-full wpuf-h-screen wpuf-overflow-hidden wpuf-relative" + role="status" :aria-busy="isProcessing.toString()">
85-85: Decorative confetti image should have empty altAvoids unnecessary announcements by screen readers.
Apply:
- <img :src="confettiUrl" alt="Confetti" class="wpuf-w-full wpuf-h-full wpuf-object-cover"/> + <img :src="confettiUrl" alt="" class="wpuf-w-full wpuf-h-full wpuf-object-cover" />
92-115: Declare emitted eventsDeclare
generation-completefor better DX and Vue 3 compatibility (dev warnings).Apply:
export default { name: 'FormProcessingStage', + emits: ['generation-complete'], props: {
7-16: Inline base64 image in the SVG is very large; consider replacingThe embedded 276Γ276 PNG inflates bundle size. Prefer a lightweight SVG/CSS spinner or a small external asset processed by Vite.
I can provide a minimal CSS spinner replacement if helpful.
vite.config.mjs (1)
21-47: Set an explicit build target to ensure optional chaining is transpiledThe component uses optional chaining (
?.). Explicitbuild.targethelps guarantee compatibility with older admin browsers if needed.Apply:
build: { + target: 'es2018', rollupOptions: {wpuf.php (1)
165-166: Prefer WPUF_INCLUDES and keep includes consistentUse the existing WPUF_INCLUDES constant for consistency with the rest of the codebase.
- require_once __DIR__ . '/includes/AI_Manager.php'; + require_once WPUF_INCLUDES . '/AI_Manager.php';assets/js/wpuf-admin.js (1)
235-237: Localize βSelect AI ModelβHard-coded English string. Use wp.i18n.__ or a localized string injected from PHP.
test-ai-context.php (2)
99-103: Normalize provider usage or update the commentComment says βTest with Google Geminiβ but provider is taken from settings. Either set provider explicitly or adjust the label.
Apply one:
-// Test with Google Gemini +// Test with current provider configured in WPUF settingsOr:
$options = [ 'conversation_context' => $scenario['context'], - 'session_id' => 'test_session_' . time() + 'session_id' => 'test_session_' . time(), + 'provider' => 'google' ];
101-101: Use a collision-resistant session idtime() can collide within a loop. Prefer uniqid with entropy.
- 'session_id' => 'test_session_' . time() + 'session_id' => uniqid('test_session_', true)includes/Admin/template-parts/ai-form-builder.php (3)
27-29: Preserve punctuation/newlines for prompt/descriptionPrompts/descriptions can be multi-line; sanitize_textarea_field keeps intent while preventing XSS.
-$description = isset( $_GET['description'] ) && ! empty( $_GET['description'] ) ? sanitize_text_field( wp_unslash( $_GET['description'] ) ) : ''; -$prompt = isset( $_GET['prompt'] ) && ! empty( $_GET['prompt'] ) ? sanitize_text_field( wp_unslash( $_GET['prompt'] ) ) : ''; +$description = isset( $_GET['description'] ) && ! empty( $_GET['description'] ) ? sanitize_textarea_field( wp_unslash( $_GET['description'] ) ) : ''; +$prompt = isset( $_GET['prompt'] ) && ! empty( $_GET['prompt'] ) ? sanitize_textarea_field( wp_unslash( $_GET['prompt'] ) ) : '';
38-43: Harden localized URLsMinor: mirror rest_url escaping for siteUrl as well.
- 'siteUrl' => site_url(), + 'siteUrl' => esc_url_raw( site_url() ),
57-64: Tailwind arbitrary class safelistingwpuf-pr-[20px] may be purged by JIT if not safelisted. Ensure itβs included in Tailwind safelist.
includes/AI/system-prompt.md (1)
435-454: Markdown structure nitsUse proper headings (##) instead of emphasis for sections; helps maintainers and linters (MD036).
test-google-context.php (3)
52-65: Prefer systemInstruction for Gemini system promptsPassing the entire system prompt as user content works but is suboptimal. Use systemInstruction to improve adherence.
- $body = [ - 'contents' => [ - [ - 'parts' => [ - ['text' => $full_prompt] - ] - ] - ], - 'generationConfig' => [ + $body = [ + 'systemInstruction' => [ + 'parts' => [['text' => $system_prompt]] + ], + 'contents' => [ + [ + 'parts' => [ + ['text' => (!empty($context) ? "CURRENT CONTEXT:\n" . json_encode($context, JSON_PRETTY_PRINT) . "\n\n" : "") . "User request: " . $prompt] + ] + ] + ], + 'generationConfig' => [ 'temperature' => 0.7, 'maxOutputTokens' => 2000, 'responseMimeType' => 'application/json' ] ];
73-86: Add cURL error handling and clearer API failuresImprove diagnostics for non-200 responses and cURL errors.
$response = curl_exec($ch); + if ($response === false) { + $err = curl_error($ch); + curl_close($ch); + return ['error' => true, 'message' => 'cURL error: ' . $err]; + } $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code === 200) { $data = json_decode($response, true); if (isset($data['candidates'][0]['content']['parts'][0]['text'])) { $content = $data['candidates'][0]['content']['parts'][0]['text']; return json_decode($content, true); } } - return ['error' => true, 'message' => 'API request failed']; + return ['error' => true, 'message' => 'API request failed', 'http_code' => $http_code, 'raw' => $response];
132-134: Avoid printing API keys (even partially)Remove API key echo to prevent accidental leakage in logs.
-echo "API Key: " . substr($API_KEY, 0, 10) . "...***\n"; -echo "Model: gemini-1.5-flash (Free tier)\n"; +echo "Model: gemini-1.5-flash\n";includes/Admin/template-parts/modal-v4.2.php (5)
154-161: Harden total template count when $registry is null/non-countable.count() on null emits warnings on older PHP. Guard with is_countable.
-// Base count: registry templates + blank form -$total_count = count($registry) + 1; +// Base count: registry templates + blank form +$total_count = ( isset( $registry ) && is_countable( $registry ) ? count( $registry ) : 0 ) + 1;
197-201: Localize hardβcoded strings (βBlank Formβ, βAI Formsβ).Use translation functions for title and labels.
-<a href="<?php echo esc_url( $blank_form_url ); ?>" class="wpuf-btn-secondary ... " title="<?php echo esc_attr( 'Blank Form' ); ?>"> +<a href="<?php echo esc_url( $blank_form_url ); ?>" class="wpuf-btn-secondary ... " title="<?php echo esc_attr__( 'Blank Form', 'wp-user-frontend' ); ?>"> ... -<p class="wpuf-text-sm ..."><?php echo esc_html( 'Blank Form' ); ?></p> +<p class="wpuf-text-sm ..."><?php echo esc_html__( 'Blank Form', 'wp-user-frontend' ); ?></p>-<a href="<?php echo esc_url( $ai_form_url ); ?>" class="wpuf-btn-secondary ... " title="<?php echo esc_attr( 'AI Forms' ); ?>"> +<a href="<?php echo esc_url( $ai_form_url ); ?>" class="wpuf-btn-secondary ... " title="<?php echo esc_attr__( 'AI Forms', 'wp-user-frontend' ); ?>"> ... -<p class="wpuf-text-sm ..."><?php echo esc_html( 'AI Forms' ); ?></p> +<p class="wpuf-text-sm ..."><?php echo esc_html__( 'AI Forms', 'wp-user-frontend' ); ?></p>Also applies to: 262-266
450-464: Remove debug console logs before release.Logs spam the console and may leak state. Gate behind a debug flag or remove.
- console.log('AI Template found:', $aiTemplate.length); - console.log('AI Configured raw:', aiConfigured); - console.log('AI Configured type:', typeof aiConfigured); ... - console.log('Attaching AI click handler because AI is NOT configured'); ... - console.log('NOT attaching AI click handler - AI is configured or template not found');
575-586: Unify βconfiguredβ detection (string vs boolean).Make the click guard consistent with init logic.
- var $template = $( e.target ).closest( '.wpuf-ai-forms-template' ); - var isConfigured = $template.data( 'ai-configured' ) === 'true'; + var $template = $( e.target ).closest( '.wpuf-ai-forms-template' ); + var raw = $template.data( 'ai-configured' ); + var isConfigured = ( raw === true || raw === 'true' );
379-409: Add modal ARIA attributes for accessibility.Role and labelling help screen readers.
-<div class="wpuf-ai-config-modal ... " id="ai-config-modal"> +<div class="wpuf-ai-config-modal ... " id="ai-config-modal" role="dialog" aria-modal="true" aria-labelledby="ai-config-modal-title"> ... - <h2 class="wpuf-text-2xl wpuf-font-medium wpuf-text-center wpuf-text-gray-900 wpuf-mb-4"> + <h2 id="ai-config-modal-title" class="wpuf-text-2xl wpuf-font-medium wpuf-text-center wpuf-text-gray-900 wpuf-mb-4">Also applies to: 390-392
assets/js/components/FormInputStage.vue (2)
123-124: Prefer wp.i18n.__ when available.Use WordPress i18n fallback chain.
- __: window.__ || ((text) => text), + __: ( (window.wp && window.wp.i18n && window.wp.i18n.__) || window.__ || ((text) => text) ),
56-62: Optional: disable button via aria and prevent double submits.Add aria-busy for accessibility and guard against double clicks.
- <button + <button @click="startGeneration" :disabled="!formDescription.trim() || isGenerating" + :aria-busy="isGenerating ? 'true' : 'false'" class="wpuf-px-8 ...includes/Admin/Forms/AI_Form_Handler.php (1)
49-54: Consistent screen IDs (nit).You use wpuf-ai-form-generation and wpuf-ai-form-generating; consider standardizing for consistency.
Also applies to: 89-94, 124-129
check-ai-status.php (1)
7-16: Optional: Gate output through WP-CLI when available.This improves UX (colors, exit codes) and avoids accidental web execution.
-echo "=== WPUF AI Form Builder Status Check ===\n\n"; +if (defined('WP_CLI') && WP_CLI) { + \WP_CLI::line('=== WPUF AI Form Builder Status Check ==='); +} else { + echo "=== WPUF AI Form Builder Status Check ===\n\n"; +}includes/AI/FormGenerator.php (2)
104-129: Keep predefined keyword logic in one place to avoid drift.isPredefinedPrompt hardcodes patterns that can diverge from PredefinedProviderβs templates. Consider delegating detection to the provider or centralizing the keyword list.
372-393: Gemini request shape: prefer system_instruction for clarity.Bundling system + user into one text works, but using system_instruction plus a separate user message tends to produce cleaner JSON and is closer to docs.
Example shape:
- system_instruction: { parts: [ { text: $system_prompt } ] }
- contents: [ { role: "user", parts: [ { text: $prompt } ] } ]
includes/Admin.php (1)
176-212: Localize all required frontend config here (good), but guard against missing settings.Looks fine. To harden:
- Cast hasApiKey to bool.
- Provide the generation route path to avoid frontend guessing.
- 'hasApiKey' => !empty($ai_settings['ai_api_key']), + 'hasApiKey' => ! empty( $ai_settings['ai_api_key'] ) ? true : false, + 'endpoint' => 'wpuf/v1/ai-form-builder/generate',AI_FORM_BUILDER_IMPLEMENTATION_PLAN.md (5)
970-1034: Make the system prompt injectable and single-sourced.Hardcoding the prompt string here and again in providers risks drift. Centralize via a single
AI_System_Prompt::get_form_generation_prompt()and reference it in all formatters/providers.
615-653: Predefined provider: avoidsleep(1)and add nonce/session validation notes.Sleeping in PHP slows admin requests. If you want simulated latency, do it in the frontend. Also add a note that session IDs are not trusted input and should be validated/sanitized.
- // Simulate processing delay (like real AI) - sleep(1); + // Frontend can simulate latency if needed; avoid delaying PHP requests.
707-730: Avoid logging full provider error bodies in production.Bodies may include provider error details that can leak into logs. Log sanitized summaries and attach the raw body only when
WP_DEBUG_LOGis true.- error_log('WPUF AI OpenAI HTTP Error: ' . $status_code . ' - ' . $error_body); + if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { + error_log('WPUF AI OpenAI HTTP Error: ' . $status_code); + }Also applies to: 721-725
1533-1535: Rate limiting listed as TODOβdocument minimal strategy.Given API cost and abuse risks, include a short section recommending per-user/hour limits via transients keyed by user ID and provider.
1776-1811: Field-structure notes: ensure ToC and taxonomy examples reflect required keys.Great that you called out
tocandtaxonomyspecifics. Add quick JSON examples showing the minimal valid WPUF payload for each to reduce ambiguity for implementers.Also applies to: 1812-1826
includes/AI/wpuf-post-form-fields-reference.md (3)
370-372: Free vs Pro classification conflicts (multiple_select, recaptcha, google_map, etc.).
multiple_selectlisted as Free here but PRO earlier;google_maplisted as PRO but appears in βReal Estateβ examples; confirm actual SKU gating and unify labels. Mark disputed items with a single βFree/Proβ source of truth.Also applies to: 392-418
421-448: Meta key rules: tighten regex guidance.Docs say βOnly letters and underscores allowed,β but examples include numbers; adjust rule to βlowercase letters, numbers, underscores,β and provide the actual regex youβll apply during normalization.
- **Characters**: Only letters and underscores allowed + **Characters**: Lowercase letters, numbers, and underscores allowed + **Validation regex**: `/^[a-z0-9_]+$/`Also applies to: 449-455
1236-1345: Settings taxonomy: label a few as Pro-only.Some settings (payments, subscription packs, conditional logic) are Pro. Add explicit βProβ badges to those keys to guide AI output and UI expectations.
Also applies to: 1348-1367
Gruntfile.js (4)
6-8: Tailwind map key is unused; duplication will run the same build twice.
tailwind()ignores the left-hand key and builds per CSS filename. Mapping two different keys to the same CSS (admin/form-builder.css) triggers duplicate builds. Consider unique CSS targets only.- 'admin/form-builder/views/form-builder-v4.1.php': 'admin/form-builder.css', - 'admin/form-builder/views/post-form-settings.php': 'admin/form-builder.css', - 'assets/js/forms-list.js': 'admin/form-builder.css', + 'admin/form-builder': 'admin/form-builder.css',
125-134: Overlapping watchers may cause redundant builds.
aiFormBuilderVueandaiFormBuilderboth watchassets/js/components/**/*.vue. This will run bothnpm_build_ai_form_builderand Tailwind builds on the same change. If intentional, ignore; otherwise, split globs or use a single watcher that chains both tasks.- 'assets/js/components/**/*.vue', + // Vue files handled by aiFormBuilderVue watcherAlso applies to: 152-162
301-316: build-tailwind runs ai-form-builder twice.
build-tailwindfirst buildsai-form-builder.css, then callstailwindwhich also builds it viatailwindFileMap. Drop the first step to avoid duplicate work.- grunt.registerTask( 'build-tailwind', [ 'shell:tailwind:src/css/ai-form-builder.css:assets/css/ai-form-builder.css', 'tailwind' ] ); + grunt.registerTask( 'build-tailwind', [ 'tailwind' ] );
323-339: Tailwind task: guard against missing Tailwind binary and add failure feedback.If
npx tailwindcssis missing, Grunt fails late. Consider pre-checking and logging a clearer message, or document the requirement in CONTRIBUTING.includes/AI_Manager.php (1)
45-48: Redundant REST API initialization hookThe
init_rest_apimethod is registered but serves no purpose since the RestController handles its own registration in the constructor.Remove the redundant hook registration and method:
private function init_hooks() { - add_action('rest_api_init', [$this, 'init_rest_api']); add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']); add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']); }-/** - * Initialize REST API - */ -public function init_rest_api() { - // REST API is already initialized in RestController constructor - // This hook ensures it runs at the right time -}assets/js/components/AIFormBuilder.vue (2)
394-394: Pro modal auto-close timeout may be too aggressiveThe 15-second auto-close for the pro modal might not give users enough time to read and make a decision.
Consider increasing the timeout or making it configurable:
// Auto-close after 15 seconds -setTimeout(closeModal, 15000); +// Auto-close after 30 seconds to give users time to read +const autoCloseTimeout = setTimeout(closeModal, 30000); + +// Clear timeout if user interacts +overlay.addEventListener('click', () => { + clearTimeout(autoCloseTimeout); +});
535-539: TODO comment indicates incomplete implementationThe
handleSendMessagemethod has a TODO comment but no implementation.The chat message handling is not implemented. Would you like me to help implement the API call for chat functionality or create an issue to track this task?
| public function generate($prompt, $provider = 'predefined') { | ||
| switch ($provider) { | ||
| case 'predefined': | ||
| $service = new PredefinedProvider(); | ||
| break; | ||
| case 'openai': | ||
| $service = new OpenaiProvider(); | ||
| break; | ||
| default: | ||
| throw new \Exception('Unknown provider'); | ||
| } | ||
|
|
||
| return $service->generateForm($prompt); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix inconsistent provider method names (generateForm vs generate_form).
Examples use both generateForm() and generate_form(). This will lead to fatal errors when wiring providers via FormGenerator. Standardize on one name across all snippets and usages.
Apply a consistent rename:
- return $service->generateForm($prompt);
+ return $service->generate_form($prompt);π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public function generate($prompt, $provider = 'predefined') { | |
| switch ($provider) { | |
| case 'predefined': | |
| $service = new PredefinedProvider(); | |
| break; | |
| case 'openai': | |
| $service = new OpenaiProvider(); | |
| break; | |
| default: | |
| throw new \Exception('Unknown provider'); | |
| } | |
| return $service->generateForm($prompt); | |
| } | |
| public function generate($prompt, $provider = 'predefined') { | |
| switch ($provider) { | |
| case 'predefined': | |
| $service = new PredefinedProvider(); | |
| break; | |
| case 'openai': | |
| $service = new OpenaiProvider(); | |
| break; | |
| default: | |
| throw new \Exception('Unknown provider'); | |
| } | |
| return $service->generate_form($prompt); | |
| } |
π€ Prompt for AI Agents
In AI_FORM_BUILDER_IMPLEMENTATION_PLAN.md around lines 123 to 136, the provider
implementations and callers mix generateForm() and generate_form(), causing
fatal method-not-found errors; standardize on a single method name (use
generateForm) by renaming generate_form() to generateForm() in all provider
classes, update any interfaces/abstract classes to declare generateForm(), and
update all call sites (including FormGenerator and examples) to invoke
generateForm(); run a quick grep across the repo to ensure no remaining
generate_form references and run tests.
| class AI_Response_Parser { | ||
| public function parse_response($provider, $response) { | ||
| $method = 'parse_' . $provider . '_response'; | ||
|
|
||
| if (method_exists($this, $method)) { | ||
| return $this->$method($response); | ||
| } | ||
|
|
||
| return $this->parse_generic_response($response); | ||
| } | ||
|
|
||
| private function parse_openai_response($response) { | ||
| $data = json_decode($response, true); | ||
|
|
||
| if (isset($data['choices'][0]['message']['content'])) { | ||
| $content = $data['choices'][0]['message']['content']; | ||
| return $this->extract_json_from_content($content); | ||
| } | ||
|
|
||
| throw new Exception('Invalid OpenAI response format'); | ||
| } | ||
|
|
||
| private function parse_anthropic_response($response) { | ||
| $data = json_decode($response, true); | ||
|
|
||
| if (isset($data['content'][0]['text'])) { | ||
| $content = $data['content'][0]['text']; | ||
| return $this->extract_json_from_content($content); | ||
| } | ||
|
|
||
| throw new Exception('Invalid Anthropic response format'); | ||
| } | ||
|
|
||
| private function parse_google_response($response) { | ||
| $data = json_decode($response, true); | ||
|
|
||
| if (isset($data['candidates'][0]['content']['parts'][0]['text'])) { | ||
| $content = $data['candidates'][0]['content']['parts'][0]['text']; | ||
| return $this->extract_json_from_content($content); | ||
| } | ||
|
|
||
| throw new Exception('Invalid Google response format'); | ||
| } | ||
|
|
||
| private function parse_cohere_response($response) { | ||
| $data = json_decode($response, true); | ||
|
|
||
| if (isset($data['text'])) { | ||
| $content = $data['text']; | ||
| return $this->extract_json_from_content($content); | ||
| } | ||
|
|
||
| throw new Exception('Invalid Cohere response format'); | ||
| } | ||
|
|
||
| private function extract_json_from_content($content) { | ||
| // Try to extract JSON from the content | ||
| if (preg_match('/\{.*\}/s', $content, $matches)) { | ||
| $json = json_decode($matches[0], true); | ||
| if (json_last_error() === JSON_ERROR_NONE) { | ||
| return $json; | ||
| } | ||
| } | ||
|
|
||
| // Try to parse the entire content as JSON | ||
| $json = json_decode($content, true); | ||
| if (json_last_error() === JSON_ERROR_NONE) { | ||
| return $json; | ||
| } | ||
|
|
||
| throw new Exception('Could not extract valid JSON from response'); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
JSON extraction via regex is brittle.
preg_match('/\{.*\}/s', $content, ...) can over-capture nested braces and fail on code fences. Prefer streaming parse with first valid-JSON slice or require tool-JSON via provider βjson modeβ.
Example approach:
- if (preg_match('/\{.*\}/s', $content, $matches)) {
- $json = json_decode($matches[0], true);
- ...
- }
+ $candidates = [$content];
+ if (preg_match_all('/```json\s*(\{.*?\})\s*```/s', $content, $m)) {
+ $candidates = array_merge($m[1], $candidates);
+ }
+ foreach ($candidates as $c) {
+ $json = json_decode(trim($c), true);
+ if (json_last_error() === JSON_ERROR_NONE) { return $json; }
+ }π€ Prompt for AI Agents
In AI_FORM_BUILDER_IMPLEMENTATION_PLAN.md around lines 315-387, the
extract_json_from_content implementation uses a single greedy regex
(preg_match('/\{.*\}/s',...)) which is brittle and will over-capture nested
braces or miss JSON inside code fences; replace it with a robust extraction
routine that: 1) first scans for explicit JSON code fences and ```json blocks
(collect all matches), 2) then collects non-greedy brace-delimited candidates
(e.g. small /{.*?}/s matches), and 3) as a fallback perform a balanced-brace
scanner that iterates from each '{' and advances a brace counter to find the
matching closing '}' to produce exact slices; iterate over all collected
candidates, trim and json_decode each, returning the first one where
json_last_error() === JSON_ERROR_NONE, and only if none succeed throw the
exception.
| private function addWPUFDefaults($form_data) { | ||
| // Add WPUF-specific settings | ||
| $form_data['form_settings'] = array_merge([ | ||
| 'form_title' => $form_data['form_title'] ?? 'AI Generated Form', | ||
| 'submit_text' => 'Submit', | ||
| 'success_message' => 'Form submitted successfully!', | ||
| 'form_template' => 'default' | ||
| ], $form_data['form_settings'] ?? []); | ||
|
|
||
| return $form_data; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Normalizer: field IDs and template mapping.
uniqid()yields string IDs; WPUF core often expects numeric keys for field arrays. Consider using incremental integers.- Some mappings (
email_address,image_upload, taxonomy) needinput_type/templatepairs to render correctly. Extend the mapping to set both when required.
- if (!isset($field['id'])) { $field['id'] = uniqid(); }
+ static $nextId = 1;
+ if (!isset($field['id'])) { $field['id'] = $nextId++; }
- if (isset($type_mapping[$field['type']])) { $field['template'] = $type_mapping[$field['type']]; }
+ if (isset($type_mapping[$field['type']])) {
+ $field['template'] = $type_mapping[$field['type']];
+ // ensure input_type for complex templates
+ if (!isset($field['input_type'])) {
+ $field['input_type'] = $field['type'];
+ }
+ }Also applies to: 486-491
π€ Prompt for AI Agents
In AI_FORM_BUILDER_IMPLEMENTATION_PLAN.md around lines 484 to 495, the
normalizer uses uniqid() string IDs and incomplete mappings which WPUF expects
numeric field keys and paired input_type/template values; replace uniqid() usage
with an incremental integer counter for field array keys (ensure uniqueness
within the form by starting from 1 or max existing key +1), and extend the
mapping logic so that fields like email_address, image_upload and taxonomy set
both the input_type and the corresponding template (or form_template) entries so
WPUF can render them properly.
| throw new Exception('Unknown provider: ' . $this->current_provider); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Qualify Exception in namespaced code.
Within namespace WeDevs\Wpuf\AI;, throw new Exception(...) resolves to WeDevs\Wpuf\AI\Exception. Use \Exception (or import) to avoid runtime errors.
- throw new Exception('Unknown provider: ' . $this->current_provider);
+ throw new \Exception('Unknown provider: ' . $this->current_provider);
- throw new Exception('API request failed: ' . $response->get_error_message());
+ throw new \Exception('API request failed: ' . $response->get_error_message());
- throw new Exception('API returned error ' . $status_code . ': ' . $body);
+ throw new \Exception('API returned error ' . $status_code . ': ' . $body);Also applies to: 949-951, 957-960
π€ Prompt for AI Agents
In AI_FORM_BUILDER_IMPLEMENTATION_PLAN.md around lines 881-882 (and similarly at
949-951 and 957-960), the code throws Exception unqualified inside namespace
WeDevs\Wpuf\AI which resolves to WeDevs\Wpuf\AI\Exception and can cause runtime
errors; update each throw to reference the global Exception class by using
\Exception (or add a use statement / import for Exception) so the thrown
exception resolves correctly.
| class WPUF_AI_REST_Controller { | ||
|
|
||
| private $namespace = 'wpuf/v1'; | ||
| private $rest_base = 'ai-form-builder'; | ||
|
|
||
| public function __construct() { | ||
| add_action('rest_api_init', [$this, 'register_routes']); | ||
| } | ||
|
|
||
| public function register_routes() { | ||
| // Generate form endpoint | ||
| register_rest_route($this->namespace, '/' . $this->rest_base . '/generate', [ | ||
| 'methods' => WP_REST_Server::CREATABLE, | ||
| 'callback' => [$this, 'generate_form'], | ||
| 'permission_callback' => [$this, 'check_permission'], | ||
| 'args' => [ | ||
| 'prompt' => [ | ||
| 'required' => true, | ||
| 'type' => 'string', | ||
| 'sanitize_callback' => 'sanitize_text_field' | ||
| ], | ||
| 'provider' => [ | ||
| 'required' => false, | ||
| 'type' => 'string', | ||
| 'default' => 'predefined', | ||
| 'enum' => ['predefined', 'openai', 'anthropic', 'google'] | ||
| ] | ||
| ] | ||
| ]); | ||
|
|
||
| // Test connection endpoint | ||
| register_rest_route($this->namespace, '/' . $this->rest_base . '/test', [ | ||
| 'methods' => WP_REST_Server::READABLE, | ||
| 'callback' => [$this, 'test_connection'], | ||
| 'permission_callback' => [$this, 'check_permission'] | ||
| ]); | ||
| } | ||
|
|
||
| public function generate_form(WP_REST_Request $request) { | ||
| $prompt = $request->get_param('prompt'); | ||
| $provider = $request->get_param('provider'); | ||
|
|
||
| try { | ||
| // Get settings | ||
| $settings = get_option('wpuf_ai_settings', []); | ||
|
|
||
| // Use predefined provider if no API key or explicitly selected | ||
| if ($provider === 'predefined' || empty($settings['api_key'])) { | ||
| $predefined = new Predefined_AI_Provider(); | ||
| $result = $predefined->generate_form($prompt); | ||
| } else { | ||
| // Use selected provider with WordPress HTTP API | ||
| switch ($provider) { | ||
| case 'openai': | ||
| $service = new WPUF_OpenAI_Simple($settings['api_key']); | ||
| $result = $service->generate_form($prompt, $settings['model'] ?? 'gpt-3.5-turbo'); | ||
| break; | ||
|
|
||
| case 'anthropic': | ||
| $service = new WPUF_Anthropic_Simple($settings['api_key']); | ||
| $result = $service->generate_form($prompt, $settings['model'] ?? 'claude-3-haiku-20240307'); | ||
| break; | ||
|
|
||
| default: | ||
| throw new Exception('Unsupported provider: ' . $provider); | ||
| } | ||
| } | ||
|
|
||
| // Check for errors | ||
| if (isset($result['error']) && $result['error']) { | ||
| return new WP_Error( | ||
| 'generation_failed', | ||
| $result['message'] ?? 'Form generation failed', | ||
| ['status' => 400] | ||
| ); | ||
| } | ||
|
|
||
| // Return successful response | ||
| return new WP_REST_Response([ | ||
| 'success' => true, | ||
| 'data' => $result | ||
| ], 200); | ||
|
|
||
| } catch (Exception $e) { | ||
| return new WP_Error( | ||
| 'generation_error', | ||
| $e->getMessage(), | ||
| ['status' => 500] | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| public function test_connection(WP_REST_Request $request) { | ||
| $settings = get_option('wpuf_ai_settings', []); | ||
| $provider = $settings['provider'] ?? 'predefined'; | ||
|
|
||
| if ($provider === 'predefined') { | ||
| return new WP_REST_Response([ | ||
| 'success' => true, | ||
| 'provider' => 'predefined', | ||
| 'message' => 'Predefined provider is always available' | ||
| ], 200); | ||
| } | ||
|
|
||
| // Test actual API connection with minimal request | ||
| try { | ||
| $test_prompt = 'Say "connection successful"'; | ||
|
|
||
| switch ($provider) { | ||
| case 'openai': | ||
| $service = new WPUF_OpenAI_Simple($settings['api_key']); | ||
| $service->generate_form($test_prompt, 'gpt-3.5-turbo'); | ||
| break; | ||
|
|
||
| case 'anthropic': | ||
| $service = new WPUF_Anthropic_Simple($settings['api_key']); | ||
| $service->generate_form($test_prompt, 'claude-3-haiku-20240307'); | ||
| break; | ||
| } | ||
|
|
||
| return new WP_REST_Response([ | ||
| 'success' => true, | ||
| 'provider' => $provider, | ||
| 'message' => 'Connection successful' | ||
| ], 200); | ||
|
|
||
| } catch (Exception $e) { | ||
| return new WP_Error( | ||
| 'connection_failed', | ||
| $e->getMessage(), | ||
| ['status' => 500] | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| public function check_permission() { | ||
| // Check if user can manage options (admin capability) | ||
| return current_user_can('manage_options'); | ||
| } | ||
| } | ||
|
|
||
| // Initialize REST controller | ||
| add_action('init', function() { | ||
| new WPUF_AI_REST_Controller(); | ||
| }); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
REST controller class name drift vs provider classes.
This block references Predefined_AI_Provider, WPUF_OpenAI_Simple, WPUF_Anthropic_Simple but earlier snippets define PredefinedProvider, OpenaiProvider, AnthropicProvider. Harmonize names to match actual classes to prevent copy/paste implementation mistakes.
π€ Prompt for AI Agents
AI_FORM_BUILDER_IMPLEMENTATION_PLAN.md lines 1044-1189: The REST controller uses
incorrect class names (Predefined_AI_Provider, WPUF_OpenAI_Simple,
WPUF_Anthropic_Simple) that donβt match the actual provider classes
(PredefinedProvider, OpenaiProvider, AnthropicProvider); update the controller
to instantiate the real class names used elsewhere and adjust any constructor
arguments/method names to match those provider implementations (e.g., replace
Predefined_AI_Provider with PredefinedProvider, WPUF_OpenAI_Simple with
OpenaiProvider, and WPUF_Anthropic_Simple with AnthropicProvider), ensuring
generate_form/test method signatures are called with the correct parameters.
| // 1. Initial form creation | ||
| echo "User: Create a contact form\n"; | ||
| $result1 = $generator->generate_form('Create a contact form', ['conversation_context' => []]); | ||
| echo "AI: Created form with " . count($result1['fields'] ?? []) . " fields\n\n"; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Also normalize field count in the flow example
Keep the conversation example consistent with the spec.
echo "User: Create a contact form\n";
$result1 = $generator->generate_form('Create a contact form', ['conversation_context' => []]);
-echo "AI: Created form with " . count($result1['fields'] ?? []) . " fields\n\n";
+$fields1 = $result1['wpuf_fields'] ?? $result1['fields'] ?? [];
+echo "AI: Created form with " . count($fields1) . " fields\n\n";π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 1. Initial form creation | |
| echo "User: Create a contact form\n"; | |
| $result1 = $generator->generate_form('Create a contact form', ['conversation_context' => []]); | |
| echo "AI: Created form with " . count($result1['fields'] ?? []) . " fields\n\n"; | |
| // 1. Initial form creation | |
| echo "User: Create a contact form\n"; | |
| $result1 = $generator->generate_form('Create a contact form', ['conversation_context' => []]); | |
| $fields1 = $result1['wpuf_fields'] ?? $result1['fields'] ?? []; | |
| echo "AI: Created form with " . count($fields1) . " fields\n\n"; |
π€ Prompt for AI Agents
In test-ai-context.php around lines 149 to 153, the example prints the raw field
count using count($result1['fields'] ?? []), which is inconsistent with the spec
that uses normalized field lists; update the printed count to use the normalized
fields if present (for example count($result1['fields_normalized'] ??
$result1['fields'] ?? [])) so the conversation example always reflects the
spec-normalized field count.
| $API_KEY = 'YOUR_GOOGLE_GEMINI_API_KEY_HERE'; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not hardcode API keys; restrict execution
Avoid committing API key placeholders and block web access. Load from env and require CLI.
-$API_KEY = 'YOUR_GOOGLE_GEMINI_API_KEY_HERE';
+if (php_sapi_name() !== 'cli') {
+ header('HTTP/1.1 403 Forbidden');
+ exit;
+}
+$API_KEY = getenv('GOOGLE_API_KEY') ?: '';
+if ($API_KEY === '') {
+ fwrite(STDERR, "GOOGLE_API_KEY not set in environment.\n");
+ exit(1);
+}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $API_KEY = 'YOUR_GOOGLE_GEMINI_API_KEY_HERE'; | |
| // Restrict to CLI only | |
| if (php_sapi_name() !== 'cli') { | |
| header('HTTP/1.1 403 Forbidden'); | |
| exit; | |
| } | |
| // Load API key from environment | |
| $API_KEY = getenv('GOOGLE_API_KEY') ?: ''; | |
| if ($API_KEY === '') { | |
| fwrite(STDERR, "GOOGLE_API_KEY not set in environment.\n"); | |
| exit(1); | |
| } |
π€ Prompt for AI Agents
In test-google-context.php around lines 6-7, remove the hardcoded $API_KEY and
instead load the key from an environment variable (e.g. getenv or $_ENV like
GOOGLE_GEMINI_API_KEY), fail fast with a clear message and non-zero exit if the
env var is empty, and immediately prevent web execution by checking runtime
(php_sapi_name() or PHP_SAPI) and exiting unless running via CLI; ensure no
default placeholder value is committed and return a non-zero status when the key
is missing.
| } else if (isset($result['form_title'])) { | ||
| echo "Response: FORM CREATED\n"; | ||
| echo "Title: " . $result['form_title'] . "\n"; | ||
| echo "Fields: " . count($result['fields'] ?? []) . "\n"; | ||
|
|
||
| // Show field types | ||
| if (isset($result['fields'])) { | ||
| foreach ($result['fields'] as $field) { | ||
| echo " - " . $field['label'] . " (" . $field['type'] . ")\n"; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Print fields from wpuf_fields (or normalize) per spec
Align output with the system prompt by preferring wpuf_fields and falling back to fields.
- } else if (isset($result['form_title'])) {
+ } else if (isset($result['form_title'])) {
echo "Response: FORM CREATED\n";
echo "Title: " . $result['form_title'] . "\n";
- echo "Fields: " . count($result['fields'] ?? []) . "\n";
+ $fields = $result['wpuf_fields'] ?? $result['fields'] ?? [];
+ echo "Fields: " . count($fields) . "\n";
// Show field types
- if (isset($result['fields'])) {
- foreach ($result['fields'] as $field) {
- echo " - " . $field['label'] . " (" . $field['type'] . ")\n";
+ if (!empty($fields)) {
+ foreach ($fields as $field) {
+ $label = $field['label'] ?? ($field['name'] ?? 'field');
+ $type = $field['input_type'] ?? ($field['type'] ?? 'unknown');
+ echo " - " . $label . " (" . $type . ")\n";
}
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (isset($result['form_title'])) { | |
| echo "Response: FORM CREATED\n"; | |
| echo "Title: " . $result['form_title'] . "\n"; | |
| echo "Fields: " . count($result['fields'] ?? []) . "\n"; | |
| // Show field types | |
| if (isset($result['fields'])) { | |
| foreach ($result['fields'] as $field) { | |
| echo " - " . $field['label'] . " (" . $field['type'] . ")\n"; | |
| } | |
| } | |
| } else if (isset($result['form_title'])) { | |
| echo "Response: FORM CREATED\n"; | |
| echo "Title: " . $result['form_title'] . "\n"; | |
| $fields = $result['wpuf_fields'] ?? $result['fields'] ?? []; | |
| echo "Fields: " . count($fields) . "\n"; | |
| // Show field types | |
| if (!empty($fields)) { | |
| foreach ($fields as $field) { | |
| $label = $field['label'] ?? ($field['name'] ?? 'field'); | |
| $type = $field['input_type'] ?? ($field['type'] ?? 'unknown'); | |
| echo " - " . $label . " (" . $type . ")\n"; | |
| } | |
| } |
π€ Prompt for AI Agents
In test-google-context.php around lines 114 to 124, the output always uses
$result['fields'] but the system requires preferring wpuf_fields and falling
back to fields; update the logic to choose $fields = $result['wpuf_fields'] ??
$result['fields'] ?? [] for the count and iteration, print "Fields: " .
count($fields), and then loop over $fields to echo each label and type (handling
missing keys defensively), ensuring the code uses wpuf_fields when present and
only falls back to fields.
| echo "Testing Google Gemini API...\n"; | ||
| echo "Endpoint: $endpoint\n\n"; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not print endpoint with API key (secret leakage).
echo "Endpoint: $endpoint" exposes the API key in logs/CI. Mask or omit it.
-echo "Testing Google Gemini API...\n";
-echo "Endpoint: $endpoint\n\n";
+echo "Testing Google Gemini API...\n";
+echo "Endpoint: https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key=****\n\n";π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| echo "Testing Google Gemini API...\n"; | |
| echo "Endpoint: $endpoint\n\n"; | |
| echo "Testing Google Gemini API...\n"; | |
| echo "Endpoint: https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key=****\n\n"; |
π€ Prompt for AI Agents
In test-google-gemini.php around lines 59 to 61, the code prints the full
endpoint which contains the API key causing secret leakage; remove or mask the
API key before logging by either omitting the endpoint entirely or printing a
redacted version (e.g., only hostname or endpoint path and replace
query/credential parts with "***"), ensure any logging uses the sanitized value
so secrets are not exposed in stdout/CI logs.
| // Make the request | ||
| $ch = curl_init($endpoint); | ||
| curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | ||
| curl_setopt($ch, CURLOPT_POST, true); | ||
| curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($request_body)); | ||
| curl_setopt($ch, CURLOPT_HTTPHEADER, [ | ||
| 'Content-Type: application/json' | ||
| ]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Harden cURL request: timeouts, UA, and JSON headers.
Avoid hangs and improve diagnostics.
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($request_body));
-curl_setopt($ch, CURLOPT_HTTPHEADER, [
- 'Content-Type: application/json'
-]);
+curl_setopt($ch, CURLOPT_HTTPHEADER, [
+ 'Content-Type: application/json',
+ 'Accept: application/json',
+ 'User-Agent: WPUF-AI-Gemini-Test/1.0'
+]);
+curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
+curl_setopt($ch, CURLOPT_TIMEOUT, 30);π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Make the request | |
| $ch = curl_init($endpoint); | |
| curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
| curl_setopt($ch, CURLOPT_POST, true); | |
| curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($request_body)); | |
| curl_setopt($ch, CURLOPT_HTTPHEADER, [ | |
| 'Content-Type: application/json' | |
| ]); | |
| // Make the request | |
| $ch = curl_init($endpoint); | |
| curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
| curl_setopt($ch, CURLOPT_POST, true); | |
| curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($request_body)); | |
| curl_setopt($ch, CURLOPT_HTTPHEADER, [ | |
| 'Content-Type: application/json', | |
| 'Accept: application/json', | |
| 'User-Agent: WPUF-AI-Gemini-Test/1.0' | |
| ]); | |
| curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); | |
| curl_setopt($ch, CURLOPT_TIMEOUT, 30); |
π€ Prompt for AI Agents
In test-google-gemini.php around lines 62 to 69, the cURL request lacks
timeouts, a User-Agent and richer JSON/diagnostic headers which can cause hangs
and poor debugging; update the curl options to set a connect timeout and overall
timeout (e.g. CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT), ensure SSL
verification remains enabled (CURLOPT_SSL_VERIFYPEER), add a User-Agent header,
include Accept: application/json and Content-Length headers alongside
Content-Type, and consider enabling CURLOPT_FAILONERROR so HTTP errors return
and can be logged; apply these header and option changes when initializing $ch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review continued from previous batch...
| const style = document.createElement('style'); | ||
| style.id = 'wpuf-ai-error-styles'; | ||
| style.textContent = ` | ||
| .wpuf-ai-error-modal { | ||
| position: fixed; | ||
| top: 0; | ||
| left: 0; | ||
| right: 0; | ||
| bottom: 0; | ||
| z-index: 999999; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| } | ||
| .wpuf-ai-error-overlay { | ||
| position: absolute; | ||
| top: 0; | ||
| left: 0; | ||
| right: 0; | ||
| bottom: 0; | ||
| background: rgba(0, 0, 0, 0.5); | ||
| } | ||
| .wpuf-ai-error-content { | ||
| position: relative; | ||
| background: white; | ||
| padding: 30px; | ||
| border-radius: 8px; | ||
| max-width: 500px; | ||
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); | ||
| } | ||
| .wpuf-ai-error-title { | ||
| color: #dc3545; | ||
| margin: 0 0 15px 0; | ||
| font-size: 20px; | ||
| } | ||
| .wpuf-ai-error-message { | ||
| color: #333; | ||
| margin: 0 0 10px 0; | ||
| line-height: 1.5; | ||
| } | ||
| .wpuf-ai-error-hint { | ||
| color: #666; | ||
| font-style: italic; | ||
| margin: 10px 0 20px 0; | ||
| padding: 10px; | ||
| background: #f8f9fa; | ||
| border-left: 3px solid #007cba; | ||
| } | ||
| .wpuf-ai-error-close { | ||
| margin-top: 15px; | ||
| } | ||
| `; | ||
| document.head.appendChild(style); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Dynamic style injection can cause memory leaks
The error modal creates style elements dynamically but never removes them, potentially causing memory leaks with repeated errors.
Check if styles already exist before adding:
// Add styles if not already present
if (!document.getElementById('wpuf-ai-error-styles')) {
const style = document.createElement('style');
style.id = 'wpuf-ai-error-styles';
style.textContent = `
.wpuf-ai-error-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999999;
display: flex;
align-items: center;
justify-content: center;
}
/* ... rest of styles ... */
`;
document.head.appendChild(style);
+
+ // Clean up styles when component is destroyed
+ this.$once('hook:beforeDestroy', () => {
+ const styleEl = document.getElementById('wpuf-ai-error-styles');
+ if (styleEl) {
+ styleEl.remove();
+ }
+ });
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const style = document.createElement('style'); | |
| style.id = 'wpuf-ai-error-styles'; | |
| style.textContent = ` | |
| .wpuf-ai-error-modal { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| z-index: 999999; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .wpuf-ai-error-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.5); | |
| } | |
| .wpuf-ai-error-content { | |
| position: relative; | |
| background: white; | |
| padding: 30px; | |
| border-radius: 8px; | |
| max-width: 500px; | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); | |
| } | |
| .wpuf-ai-error-title { | |
| color: #dc3545; | |
| margin: 0 0 15px 0; | |
| font-size: 20px; | |
| } | |
| .wpuf-ai-error-message { | |
| color: #333; | |
| margin: 0 0 10px 0; | |
| line-height: 1.5; | |
| } | |
| .wpuf-ai-error-hint { | |
| color: #666; | |
| font-style: italic; | |
| margin: 10px 0 20px 0; | |
| padding: 10px; | |
| background: #f8f9fa; | |
| border-left: 3px solid #007cba; | |
| } | |
| .wpuf-ai-error-close { | |
| margin-top: 15px; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| } | |
| // Add styles if not already present | |
| if (!document.getElementById('wpuf-ai-error-styles')) { | |
| const style = document.createElement('style'); | |
| style.id = 'wpuf-ai-error-styles'; | |
| style.textContent = ` | |
| .wpuf-ai-error-modal { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| z-index: 999999; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .wpuf-ai-error-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.5); | |
| } | |
| .wpuf-ai-error-content { | |
| position: relative; | |
| background: white; | |
| padding: 30px; | |
| border-radius: 8px; | |
| max-width: 500px; | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); | |
| } | |
| .wpuf-ai-error-title { | |
| color: #dc3545; | |
| margin: 0 0 15px 0; | |
| font-size: 20px; | |
| } | |
| .wpuf-ai-error-message { | |
| color: #333; | |
| margin: 0 0 10px 0; | |
| line-height: 1.5; | |
| } | |
| .wpuf-ai-error-hint { | |
| color: #666; | |
| font-style: italic; | |
| margin: 10px 0 20px 0; | |
| padding: 10px; | |
| background: #f8f9fa; | |
| border-left: 3px solid #007cba; | |
| } | |
| .wpuf-ai-error-close { | |
| margin-top: 15px; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| // Clean up styles when component is destroyed | |
| this.$once('hook:beforeDestroy', () => { | |
| const styleEl = document.getElementById('wpuf-ai-error-styles'); | |
| if (styleEl) { | |
| styleEl.remove(); | |
| } | |
| }); | |
| } |
π€ Prompt for AI Agents
In assets/js/components/AIFormBuilder.vue around lines 214 to 267, the component
always injects a <style> element with id 'wpuf-ai-error-styles' when showing the
error modal, which can leak memory if done repeatedly; modify the logic to first
check document.getElementById('wpuf-ai-error-styles') and only create/append the
style element if it doesn't already exist, and ensure that when the modal is
removed or the component is unmounted you also remove the style element (or
leave it if intended to persist) to avoid orphaned elements; additionally,
consolidate style injection into a single init/cleanup path (e.g., create once
on mount and remove on unmount) rather than recreating on each error.
| */ | ||
| public function __construct() { | ||
| $this->form_generator = new FormGenerator(); | ||
| add_action('rest_api_init', [$this, 'register_routes']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RestController couples REST route registration to constructor
Registering REST routes in the constructor can cause issues if the class is instantiated before rest_api_init action fires.
Move the hook registration to a separate init method:
public function __construct() {
$this->form_generator = new FormGenerator();
- add_action('rest_api_init', [$this, 'register_routes']);
+}
+
+/**
+ * Initialize REST routes
+ */
+public function init() {
+ add_action('rest_api_init', [$this, 'register_routes']);
}And update AI_Manager.php to call init():
private function init_classes() {
// Initialize Form Generator
$this->form_generator = new FormGenerator();
// Initialize REST Controller
$this->rest_controller = new RestController();
+ $this->rest_controller->init();
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| */ | |
| public function __construct() { | |
| $this->form_generator = new FormGenerator(); | |
| add_action('rest_api_init', [$this, 'register_routes']); | |
| /** | |
| * β¦ | |
| */ | |
| public function __construct() { | |
| $this->form_generator = new FormGenerator(); | |
| } | |
| /** | |
| * Initialize REST routes | |
| */ | |
| public function init() { | |
| add_action('rest_api_init', [$this, 'register_routes']); | |
| } |
π€ Prompt for AI Agents
In includes/AI/RestController.php around lines 43-46, the REST route
registration is being hooked in the constructor which can run before
rest_api_init fires; remove the add_action('rest_api_init', [$this,
'register_routes']) call from the constructor, create a new public init() method
that calls add_action('rest_api_init', [$this, 'register_routes']), keep
instantiating FormGenerator in the constructor, and then update AI_Manager.php
to call $restController->init() (or equivalent) after constructing the
RestController so route registration happens during or after rest_api_init.
| 'provider' => [ | ||
| 'required' => true, | ||
| 'type' => 'string', | ||
| 'enum' => ['predefined', 'openai', 'anthropic'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing 'google' provider in settings endpoint enum
The generate endpoint includes 'google' as an allowed provider (line 79), but the settings endpoint doesn't (line 121).
Add the missing provider for consistency:
'provider' => [
'required' => true,
'type' => 'string',
- 'enum' => ['predefined', 'openai', 'anthropic']
+ 'enum' => ['predefined', 'openai', 'anthropic', 'google']
],π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'enum' => ['predefined', 'openai', 'anthropic'] | |
| 'provider' => [ | |
| 'required' => true, | |
| 'type' => 'string', | |
| 'enum' => ['predefined', 'openai', 'anthropic', 'google'] | |
| ], |
π€ Prompt for AI Agents
In includes/AI/RestController.php around line 121, the settings endpoint enum
omits the 'google' provider while the generate endpoint allows it; update the
enum array to include 'google' (i.e., add 'google' to the list
['predefined','openai','anthropic']) so both endpoints use the same allowed
providers.
| $max_requests = 50; // 50 requests per hour | ||
|
|
||
| if ($current_count === false) { | ||
| // First request in this hour | ||
| set_transient($rate_limit_key, 1, HOUR_IN_SECONDS); | ||
| return true; | ||
| } | ||
|
|
||
| if ($current_count >= $max_requests) { | ||
| return false; | ||
| } | ||
|
|
||
| // Increment counter | ||
| set_transient($rate_limit_key, $current_count + 1, HOUR_IN_SECONDS); | ||
| return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Rate limiting uses transients which may not be reliable
Using transients for rate limiting can be unreliable if an object cache is misconfigured or if transients are stored in memory.
Consider using user meta for more reliable rate limiting:
private function check_rate_limit() {
$user_id = get_current_user_id();
- $rate_limit_key = 'wpuf_ai_rate_limit_' . $user_id;
+ $current_hour = date('Y-m-d-H');
+ $rate_limit_meta_key = 'wpuf_ai_rate_limit';
- $current_count = get_transient($rate_limit_key);
+ $rate_data = get_user_meta($user_id, $rate_limit_meta_key, true);
+
+ if (!is_array($rate_data) || $rate_data['hour'] !== $current_hour) {
+ // New hour, reset counter
+ $rate_data = ['hour' => $current_hour, 'count' => 0];
+ }
+
$max_requests = 50; // 50 requests per hour
- if ($current_count === false) {
- // First request in this hour
- set_transient($rate_limit_key, 1, HOUR_IN_SECONDS);
- return true;
- }
-
- if ($current_count >= $max_requests) {
+ if ($rate_data['count'] >= $max_requests) {
return false;
}
- // Increment counter
- set_transient($rate_limit_key, $current_count + 1, HOUR_IN_SECONDS);
+ $rate_data['count']++;
+ update_user_meta($user_id, $rate_limit_meta_key, $rate_data);
return true;
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $max_requests = 50; // 50 requests per hour | |
| if ($current_count === false) { | |
| // First request in this hour | |
| set_transient($rate_limit_key, 1, HOUR_IN_SECONDS); | |
| return true; | |
| } | |
| if ($current_count >= $max_requests) { | |
| return false; | |
| } | |
| // Increment counter | |
| set_transient($rate_limit_key, $current_count + 1, HOUR_IN_SECONDS); | |
| return true; | |
| private function check_rate_limit() { | |
| $user_id = get_current_user_id(); | |
| $current_hour = date('Y-m-d-H'); | |
| $rate_limit_meta_key = 'wpuf_ai_rate_limit'; | |
| $rate_data = get_user_meta($user_id, $rate_limit_meta_key, true); | |
| if (! is_array($rate_data) || $rate_data['hour'] !== $current_hour) { | |
| // New hour, reset counter | |
| $rate_data = [ | |
| 'hour' => $current_hour, | |
| 'count' => 0, | |
| ]; | |
| } | |
| $max_requests = 50; // 50 requests per hour | |
| if ($rate_data['count'] >= $max_requests) { | |
| return false; | |
| } | |
| $rate_data['count']++; | |
| update_user_meta($user_id, $rate_limit_meta_key, $rate_data); | |
| return true; | |
| } |
| 'post_status' => 'publish', | ||
| 'post_parent' => $form_id, | ||
| 'menu_order' => $order, | ||
| 'post_content' => serialize($field) // WPUF stores field data as serialized content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using serialize() for data storage is a security risk
WordPress Core discourages using serialize() for storing data due to potential object injection vulnerabilities.
Use JSON encoding instead:
'post_parent' => $form_id,
'menu_order' => $order,
-'post_content' => serialize($field) // WPUF stores field data as serialized content
+'post_content' => wp_json_encode($field) // Store as JSON for securityYou'll also need to update the retrieval logic to decode JSON instead of unserializing.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'post_content' => serialize($field) // WPUF stores field data as serialized content | |
| 'post_parent' => $form_id, | |
| 'menu_order' => $order, | |
| 'post_content' => wp_json_encode( $field ) // Store as JSON for security |
π€ Prompt for AI Agents
In includes/AI/RestController.php around line 489, the code currently uses
serialize($field) to store field data which is discouraged for security; replace
serialize() with json_encode($field, JSON_UNESCAPED_UNICODE) when saving and
ensure the value is stored as a JSON string, and update all retrieval sites to
use json_decode($value, true) (or false when objects are required) instead of
unserialize(), validating/typing the decoded data before use to avoid injection
issues.
| return 'wpuf_ai_session_' . time() . '_' . wp_generate_uuid4(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Session ID generation uses wp_generate_uuid4 which may not exist
The wp_generate_uuid4() function was introduced in WordPress 4.7 but might not be available in all environments.
Add a fallback for older WordPress versions:
private function generate_session_id() {
- return 'wpuf_ai_session_' . time() . '_' . wp_generate_uuid4();
+ $unique_id = function_exists('wp_generate_uuid4')
+ ? wp_generate_uuid4()
+ : wp_generate_password(32, false);
+ return 'wpuf_ai_session_' . time() . '_' . $unique_id;
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return 'wpuf_ai_session_' . time() . '_' . wp_generate_uuid4(); | |
| } | |
| private function generate_session_id() { | |
| $unique_id = function_exists('wp_generate_uuid4') | |
| ? wp_generate_uuid4() | |
| : wp_generate_password(32, false); | |
| return 'wpuf_ai_session_' . time() . '_' . $unique_id; | |
| } |
π€ Prompt for AI Agents
In includes/AI/RestController.php around lines 782-783, the session ID
generation calls wp_generate_uuid4() which may not exist on older WP versions;
update the function to check function_exists('wp_generate_uuid4') and use it
when available, otherwise generate a safe UUID4 fallback (e.g., build a
version-4 UUID from random_bytes/openssl_random_pseudo_bytes and proper hex
formatting) or reliably fallback to uniqid combined with secure random data and
hashing to ensure uniqueness and entropy; return the same 'wpuf_ai_session_' .
time() . '_' . <fallback> value so callers are unchanged.
| 'post_status' => 'publish', | ||
| 'post_parent' => $form_id, | ||
| 'menu_order' => $order, | ||
| 'post_content' => serialize($field) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using serialize() again for field storage
Same serialization issue as mentioned earlier.
Use JSON encoding:
'post_parent' => $form_id,
'menu_order' => $order,
-'post_content' => serialize($field)
+'post_content' => wp_json_encode($field)π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'post_content' => serialize($field) | |
| 'post_parent' => $form_id, | |
| 'menu_order' => $order, | |
| 'post_content' => wp_json_encode($field) |
π€ Prompt for AI Agents
In includes/AI/RestController.php around line 903, post_content is being set
with serialize($field) which repeats the earlier serialization problem; replace
serialize($field) with a JSON encoder (preferably wp_json_encode($field) or
json_encode($field)) so the field is stored as JSON, and ensure any code that
reads this value uses json_decode (or json_decode(..., true)) instead of
unserialize.
| private function should_be_meta($field_name) { | ||
| $post_fields = ['post_title', 'post_content', 'post_excerpt', 'post_tags', 'post_category']; | ||
| return !in_array($field_name, $post_fields); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused private method
The should_be_meta method is never called.
Remove the unused method:
-/**
- * Determine if field should be meta
- *
- * @param string $field_name
- * @return bool
- */
-private function should_be_meta($field_name) {
- $post_fields = ['post_title', 'post_content', 'post_excerpt', 'post_tags', 'post_category'];
- return !in_array($field_name, $post_fields);
-}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private function should_be_meta($field_name) { | |
| $post_fields = ['post_title', 'post_content', 'post_excerpt', 'post_tags', 'post_category']; | |
| return !in_array($field_name, $post_fields); | |
| } | |
| // (The unused should_be_meta method has been removed.) |
π§° Tools
πͺ PHPMD (2.15.0)
972-975: Avoid unused private methods such as 'should_be_meta'. (Unused Code Rules)
(UnusedPrivateMethod)
π€ Prompt for AI Agents
In includes/AI/RestController.php around lines 972 to 975, the private method
should_be_meta(...) is unused; remove the entire private method declaration
(including its signature and body) from the file to clean up dead code, and run
a quick project-wide search to confirm there are no remaining references before
committing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
β»οΈ Duplicate comments (6)
includes/AI/system-prompt.md (1)
16-20: Remove line comments from JSON example; they produce invalid JSON and bias models.Keep examples strictly valid JSON; move guidance into surrounding text.
Apply this diff:
"wpuf_fields": [ - // REQUIRED: Array of complete field objects - // MUST include post_title as first field - // MUST include post_content as second field - // Then additional fields ],includes/Admin.php (1)
45-46: Hook is registered but never fired; localization wonβt run, breaking the AI UI.enqueue_ai_form_builder_scripts wonβt execute unless something calls
do_action( 'wpuf_load_ai_form_builder_page' ). AI_Form_Handler directly enqueues scripts and never fires the hook, sowpufAIFormBuilderisnβt localized and the Vue app lacks config.Apply this diff in includes/Admin/Forms/AI_Form_Handler.php to centralize enqueues and ensure localization:
@@ public function handle_ai_form_template() { - // Load CSS - wp_enqueue_style( 'wpuf-ai-form-builder' ); - // Load scripts - wp_enqueue_script( 'wpuf-ai-form-builder' ); + // Localize + enqueue via admin hook + do_action( 'wpuf_load_ai_form_builder_page' ); @@ public function handle_ai_form_generating() { - // Load CSS and scripts - wp_enqueue_style( 'wpuf-ai-form-builder' ); - wp_enqueue_script( 'wpuf-ai-form-builder' ); + do_action( 'wpuf_load_ai_form_builder_page' ); @@ public function handle_ai_form_success() { - // Load CSS and scripts - wp_enqueue_style( 'wpuf-ai-form-builder' ); - wp_enqueue_script( 'wpuf-ai-form-builder' ); + do_action( 'wpuf_load_ai_form_builder_page' );includes/AI/wpuf-post-form-fields-reference.md (1)
83-85: Fix βtocβ description: itβs Terms and Conditions, not Table of Contents.Align with WPUF nomenclature to avoid mis-generation.
Apply:
-| `toc` | Table of Contents | Generate table of contents | ... +| `toc` | Terms and Conditions | Consent checkbox with full terms text | ...assets/js/components/AIFormBuilder.vue (2)
211-266: Clean up dynamically injected styles to prevent leaks.The modal stylesheet persists across component lifecycles.
Add a lifecycle cleanup:
export default { name: 'AIFormBuilder', + beforeDestroy() { + const styleEl = document.getElementById('wpuf-ai-error-styles'); + if (styleEl) styleEl.remove(); + },Also applies to: 634-657
409-414: Generate a cryptographically strong session ID.Use crypto.randomUUID()/getRandomValues; avoid predictable Math.random() + timestamp.
getSessionId() { if (!this.sessionId) { - this.sessionId = 'wpuf_ai_session_' + Date.now() + '_' + Math.random().toString(36).substring(2, 11); + const uuid = (window.crypto && window.crypto.randomUUID) + ? window.crypto.randomUUID() + : (window.crypto && window.crypto.getRandomValues) + ? Array.from(window.crypto.getRandomValues(new Uint8Array(16))).map(b => b.toString(16).padStart(2,'0')).join('') + : (Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2)); + this.sessionId = 'wpuf_ai_session_' + uuid; } return this.sessionId; },includes/functions/settings-options.php (1)
688-693: Mask and sanitize API key.Render as password; sanitize on save to reduce accidental exposure.
[ 'name' => 'ai_api_key', 'label' => __( 'API Key', 'wp-user-frontend' ), 'desc' => __( 'Enter your AI service API key. Keep this secure and never share it publicly.', 'wp-user-frontend' ), - 'type' => 'text', + 'type' => 'password', 'default' => '', ],Also ensure sanitization via sanitize_text_field on save (settings API callback) and store with autoload = no.
π§Ή Nitpick comments (7)
includes/AI/system-prompt.md (1)
112-112: Minor: avoid emphasis-as-heading.Change the emphasized warning to a proper heading or normal paragraph to satisfy MD036.
includes/Admin.php (1)
176-212: Surface temperature/maxTokens from settings instead of hardcoding.Respect saved admin settings; keep safe defaults.
Apply:
- 'temperature' => 0.7, - 'maxTokens' => 2000, + 'temperature' => isset( $ai_settings['temperature'] ) ? (float) $ai_settings['temperature'] : 0.7, + 'maxTokens' => isset( $ai_settings['max_tokens'] ) ? (int) $ai_settings['max_tokens'] : 2000,includes/AI/wpuf-post-form-fields-reference.md (1)
67-75: Add blank lines around tables to satisfy MD058.Insert a blank line before and after each table block.
Also applies to: 79-87
Lib/AI/PredefinedProvider.php (2)
715-716: Avoid artificial 1s delay in production.sleep(1) slows the UI. Gate behind a constant or WP_DEBUG.
- sleep(1); + if (defined('WPUF_AI_DEV_DELAY') && WPUF_AI_DEV_DELAY) { + sleep(1); + }
803-805: Prefix global WP functions inside namespaces.Use leading backslash for __(), get_option(), current_time() to avoid namespaced lookups.
Example:
- $response['generated_at'] = current_time('mysql'); + $response['generated_at'] = \current_time('mysql'); ... - $wpuf_field['first'] = __('- Select -', 'wp-user-frontend'); + $wpuf_field['first'] = \__('- Select -', 'wp-user-frontend');Also applies to: 1024-1054, 1119-1166, 1202-1249
includes/AI/FormGenerator.php (1)
54-60: Align OpenAI model list with settings UI.Settings include gpt-4o-2024-08-06 and o1-* but provider_configs doesnβt. Keep them in sync to avoid confusing UIs.
'models' => [ 'gpt-4o' => 'GPT-4o', 'gpt-4o-mini' => 'GPT-4o Mini', + 'gpt-4o-2024-08-06' => 'GPT-4o (2024-08-06)', 'gpt-4-turbo' => 'GPT-4 Turbo', 'gpt-4' => 'GPT-4', 'gpt-3.5-turbo' => 'GPT-3.5 Turbo' ],If supporting o1/o3 models, route them via the Responses API instead of Chat Completions to prevent 400s.
includes/functions/settings-options.php (1)
646-651: Offer a βPredefined (Testing)β provider in the UI.It exists in the backend and is useful when no keys are set.
'options' => [ 'openai' => 'OpenAI', 'anthropic' => 'Anthropic', - 'google' => 'Google', + 'google' => 'Google', + 'predefined'=> __('Predefined (Testing)', 'wp-user-frontend'), ],
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
β Files ignored due to path filters (1)
assets/js/ai-form-builder.min.jsis excluded by!**/*.min.js
π Files selected for processing (7)
Lib/AI/PredefinedProvider.php(1 hunks)assets/js/components/AIFormBuilder.vue(1 hunks)includes/AI/FormGenerator.php(1 hunks)includes/AI/system-prompt.md(1 hunks)includes/AI/wpuf-post-form-fields-reference.md(1 hunks)includes/Admin.php(4 hunks)includes/functions/settings-options.php(2 hunks)
π§° Additional context used
𧬠Code graph analysis (2)
includes/AI/FormGenerator.php (2)
Lib/AI/PredefinedProvider.php (2)
PredefinedProvider(13-1251)generateForm(713-821)includes/AI/RestController.php (4)
__construct(44-47)generate_form(203-256)get_providers(284-293)test_connection(264-276)
includes/Admin.php (2)
includes/Admin/Forms/AI_Form_Handler.php (2)
AI_Form_Handler(12-140)handle_ai_form_template(27-64)Lib/WeDevs_Settings_API.php (1)
get_option(559-568)
πͺ markdownlint-cli2 (0.17.2)
includes/AI/system-prompt.md
112-112: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
includes/AI/wpuf-post-form-fields-reference.md
67-67: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
79-79: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
190-190: Multiple headings with the same content
(MD024, no-duplicate-heading)
π Additional comments (3)
includes/AI/wpuf-post-form-fields-reference.md (1)
58-62: All referenced field templates exist in code; no removals required.includes/AI/FormGenerator.php (1)
219-243: OpenAI: consider Responses API for o1/o3 models and JSON Mode reliability.Current implementation uses chat/completions with response_format. Some models require /v1/responses.
Would you like a follow-up patch that detects o1*/o3* model ids and switches endpoints/payload accordingly?
includes/functions/settings-options.php (1)
660-686: Model list/UI drifts from backend support.UI lists o1-preview/o1-mini and gpt-4o-2024-08-06; provider_configs doesnβt. Align or filter options per provider.
I can add a small helper to expose FormGenerator::get_providers() to the settings page and render models dynamically; want me to prepare that?
| const response = await fetch(restUrl + 'wpuf/v1/ai-form-builder/generate', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'X-WP-Nonce': nonce | ||
| }, | ||
| body: JSON.stringify({ | ||
| prompt: prompt, | ||
| session_id: this.getSessionId(), | ||
| provider: config.provider || 'predefined', | ||
| temperature: config.temperature || 0.7, | ||
| max_tokens: config.maxTokens || 2000 | ||
| }) | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Improve non-OK response handling: include server error body.
Currently throws with statusText only; WP errors return useful JSON. Parse JSON (fall back to text) for better UX.
- if (!response.ok) {
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
- }
+ if (!response.ok) {
+ let detail = '';
+ try {
+ const errJson = await response.json();
+ detail = errJson?.message || JSON.stringify(errJson);
+ } catch (e) {
+ detail = await response.text().catch(() => '');
+ }
+ throw new Error(`HTTP ${response.status}: ${detail || response.statusText}`);
+ }Also applies to: 132-136, 166-177
π€ Prompt for AI Agents
In assets/js/components/AIFormBuilder.vue around lines 115-128 (and similarly at
132-136 and 166-177), the fetch error handling only throws response.statusText
which discards helpful JSON error payloads from WP; update the logic to when
response.ok is false attempt to parse await response.json() inside a try/catch
(fall back to await response.text() if JSON parsing fails), then include that
parsed body (or text) in the thrown Error or rejected object so callers can show
the server error details; ensure you maintain the existing status/statusText
fields when constructing the error to preserve context.
| // Create a modal-like error display | ||
| const errorContainer = document.createElement('div'); | ||
| errorContainer.className = 'wpuf-ai-error-modal'; | ||
| errorContainer.innerHTML = ` | ||
| <div class="wpuf-ai-error-overlay"></div> | ||
| <div class="wpuf-ai-error-content"> | ||
| <h3 class="wpuf-ai-error-title"> | ||
| ${errorType === 'invalid_request' ? (i18n.invalidRequest || 'Invalid Request') : (i18n.errorTitle || 'Error')} | ||
| </h3> | ||
| <p class="wpuf-ai-error-message">${message}</p> | ||
| ${errorType === 'invalid_request' ? ` | ||
| <p class="wpuf-ai-error-hint"> | ||
| ${i18n.nonFormRequest || 'I can only help with form creation. Try: "Create a contact form"'} | ||
| </p> | ||
| ` : ''} | ||
| <button class="wpuf-ai-error-close button button-primary"> | ||
| ${i18n.tryAgain || 'Try Again'} | ||
| </button> | ||
| </div> | ||
| `; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Block XSS: sanitize server-provided message before injecting innerHTML.
message is inserted via template literals into innerHTML. If the backend returns HTML, this is a stored/reflective XSS in wp-admin. Sanitize or set textContent instead.
Apply this diff to add a sanitizer and use it here:
+ sanitizeHtml(str) {
+ const div = document.createElement('div');
+ div.textContent = String(str ?? '');
+ return div.innerHTML;
+ },
...
handleGenerationError(message, errorType = 'general') {
...
- errorContainer.innerHTML = `
+ const safeMessage = this.sanitizeHtml(message);
+ errorContainer.innerHTML = `
...
- <p class="wpuf-ai-error-message">${message}</p>
+ <p class="wpuf-ai-error-message">${safeMessage}</p>Also applies to: 211-266
| if (result.form_data) { | ||
| this.generatedFormData = result.form_data; | ||
| this.formTitle = result.form_data.form_title || 'Generated Form'; | ||
| // For preview, use fields (simplified), but store wpuf_fields for actual form creation | ||
| this.formFields = this.convertFieldsToPreview(result.form_data.fields || []); | ||
|
|
||
| setTimeout(() => { | ||
| this.handleGenerationComplete(); | ||
|
|
||
| // Only show pro field warning if Pro is not active | ||
| if (!isProActive) { | ||
| setTimeout(() => { | ||
| this.showProFieldModal(result.message); | ||
| }, 500); | ||
| } else { | ||
| console.log('Pro is active, no need to show warning'); | ||
| } | ||
| }, 1000); | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undefined method convertFieldsToPreview causes runtime error; also breaks WPUF format.
This path calls an undefined method and converts away from WPUF format, but later code assumes WPUF fields.
Apply:
- // For preview, use fields (simplified), but store wpuf_fields for actual form creation
- this.formFields = this.convertFieldsToPreview(result.form_data.fields || []);
+ // Keep WPUF format to ensure create/edit works
+ this.formFields = result.form_data.wpuf_fields || result.form_data.fields || [];Also applies to: 290-291
π€ Prompt for AI Agents
In assets/js/components/AIFormBuilder.vue around lines 286 to 305 (also touching
290-291), the code calls an undefined convertFieldsToPreview and overwrites WPUF
field format causing runtime errors and later assumptions to break; replace that
call so generatedFormData preserves the original WPUF fields and only derive a
separate preview structure if needed. Specifically, remove the call to
convertFieldsToPreview and set this.formFields = result.form_data.wpuf_fields ||
result.form_data.fields || []; if a simplified preview is required, implement a
new helper (e.g., buildPreviewFields) that returns a copy derived from
wpuf_fields and assign it to a new property like this.previewFields, leaving
this.formFields as the WPUF-compatible array; ensure any references later in the
component use the correct property.
| modal.innerHTML = ` | ||
| <div style="padding: 24px;"> | ||
| <div style="display: flex; align-items: center; margin-bottom: 16px;"> | ||
| <span style="color: #f39c12; font-size: 24px; margin-right: 12px;">β‘</span> | ||
| <h3 style="margin: 0; font-size: 20px; color: #333;">${i18n.proFieldWarning || 'Pro Feature Required'}</h3> | ||
| </div> | ||
| <p style="color: #666; margin-bottom: 20px; line-height: 1.5;"> | ||
| ${i18n.proFieldMessage || 'This field type requires WP User Frontend Pro. You can continue without it or upgrade to Pro for full functionality.'} | ||
| </p> | ||
| <div style="background: #f8f9fa; padding: 12px; border-radius: 4px; margin-bottom: 20px; font-size: 14px; color: #666;"> | ||
| ${message} | ||
| </div> | ||
| <div style="display: flex; gap: 12px; justify-content: flex-end;"> | ||
| <button id="wpuf-continue-without-pro" style=" | ||
| padding: 8px 16px; | ||
| border: 1px solid #ddd; | ||
| background: white; | ||
| color: #666; | ||
| border-radius: 4px; | ||
| cursor: pointer; | ||
| font-size: 14px; | ||
| ">${i18n.continueWithoutPro || 'Continue without Pro'}</button> | ||
| <a href="https://wedevs.com/wp-user-frontend-pro/pricing/" target="_blank" style=" | ||
| padding: 8px 16px; | ||
| background: #0073aa; | ||
| color: white; | ||
| text-decoration: none; | ||
| border-radius: 4px; | ||
| font-size: 14px; | ||
| ">${i18n.upgradeToPro || 'Upgrade to Pro'}</a> | ||
| </div> | ||
| </div> | ||
| `; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Block XSS in Pro modal too.
The user-facing message is injected into innerHTML unsanitized.
Use the same sanitizer:
- modal.innerHTML = `
+ const safeProMsg = this.sanitizeHtml(message);
+ modal.innerHTML = `
...
- <div style="background: #f8f9fa; padding: 12px; border-radius: 4px; margin-bottom: 20px; font-size: 14px; color: #666;">
- ${message}
- </div>
+ <div style="background: #f8f9fa; padding: 12px; border-radius: 4px; margin-bottom: 20px; font-size: 14px; color: #666;">${safeProMsg}</div>Also applies to: 374-393
π€ Prompt for AI Agents
In assets/js/components/AIFormBuilder.vue around lines 340-372 (and likewise
apply the same change to lines 374-393), the modal HTML builds innerHTML with
unsanitized user-facing strings (e.g., message and i18n values) which allows
XSS; sanitize these values using the same project sanitizer function before
interpolation (escape or strip unsafe HTML), and then construct the modal markup
with the sanitized variables (or insert text nodes via DOM APIs instead of
innerHTML) so no raw user input is injected into innerHTML.
| const fieldsList = this.formFields.map(field => { | ||
| const requiredText = (field.required === 'yes' || field.required === true) ? ' (Required)' : ''; | ||
| const fieldType = this.getFieldDisplayType(field); | ||
| return `<li>${field.label}${requiredText} - ${this.getFieldTypeDescription(fieldType)}</li>`; | ||
| }).join(''); | ||
|
|
||
| const successMessage = { | ||
| type: 'ai', | ||
| content: `Perfect! I've created a "${this.formTitle}" form for you with the following fields: | ||
| <ul>${fieldsList}</ul> | ||
| ${this.generatedFormData.form_description || 'The form is ready and you can customize it further in the form builder!'}`, | ||
| showButtons: false, // Don't show buttons for initial form creation | ||
| status: 'Successfully created the form.' | ||
| }; | ||
|
|
||
| this.chatMessages.push(successMessage); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π‘ Verification agent
π§© Analysis chain
Sanitize field labels used in HTML chat content.
Field labels from AI/server are interpolated into an HTML string. If FormSuccessStage renders via v-html, thatβs XSS.
- Sanitize labels before concatenation:
- const fieldsList = this.formFields.map(field => {
- const requiredText = (field.required === 'yes' || field.required === true) ? ' (Required)' : '';
- const fieldType = this.getFieldDisplayType(field);
- return `<li>${field.label}${requiredText} - ${this.getFieldTypeDescription(fieldType)}</li>`;
- }).join('');
+ const fieldsList = this.formFields.map(field => {
+ const requiredText = (field.required === 'yes' || field.required === true) ? ' (Required)' : '';
+ const fieldType = this.getFieldDisplayType(field);
+ return `<li>${this.sanitizeHtml(field.label)}${requiredText} - ${this.getFieldTypeDescription(fieldType)}</li>`;
+ }).join('');Run to confirm whether v-html is used for chat content:
π Script executed:
#!/bin/bash
rg -n --type=vue -C2 'v-html=.*content|v-html=.*message' assets/js/componentsLength of output: 27
π Script executed:
#!/bin/bash
rg -n 'v-html' -C2 --glob '*.vue' assets/js/componentsLength of output: 3598
π Script executed:
#!/bin/bash
rg -n 'sanitizeHtml' -C2 --glob '*.{vue,js,ts}' assets/js/componentsLength of output: 0
Sanitize field labels before rendering via v-html
FormSuccessStage.vue (line 75) uses v-html="message.content", and that content is built in AIFormBuilder.vue (lines 430β446) by interpolating field.label into an HTML stringβthis opens an XSS vector. Wrap field.label in a sanitizer (e.g. import and use DOMPurify.sanitize(field.label)) before concatenating into fieldsList.
π€ Prompt for AI Agents
In assets/js/components/AIFormBuilder.vue around lines 430 to 446, field.label
is interpolated directly into an HTML string which is later rendered with
v-html, creating an XSS risk; import a sanitizer (e.g. DOMPurify) at the top of
the file and call DOMPurify.sanitize(field.label) (or sanitize the assembled
fieldsList or final content) before concatenating into the HTML string so only
safe HTML is produced, then push the sanitized content into
successMessage.content.
| # WPUF Post Form Fields Reference | ||
|
|
||
| This document provides a comprehensive reference for all available fields in WPUF (WordPress User Frontend) post forms, including both free and pro fields. This reference is used by the AI form creation system to understand available field types, properties, and configurations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Prevent AI from copying simplified examples into outputs.
This reference shows minimal field blobs that omit required properties; when appended to prompts, models may mimic them. Add a top-note that examples are schematic only and that generation must follow system-promptβs full schema.
Apply:
# WPUF Post Form Fields Reference
+
+Note: Examples in this document are schematic for human reference. AI outputs MUST follow the exact, fully-expanded field schema defined in includes/AI/system-prompt.md and MUST NOT copy these minimal examples verbatim.π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # WPUF Post Form Fields Reference | |
| This document provides a comprehensive reference for all available fields in WPUF (WordPress User Frontend) post forms, including both free and pro fields. This reference is used by the AI form creation system to understand available field types, properties, and configurations. | |
| # WPUF Post Form Fields Reference | |
| Note: Examples in this document are schematic for human reference. AI outputs MUST follow the exact, fully-expanded field schema defined in includes/AI/system-prompt.md and MUST NOT copy these minimal examples verbatim. | |
| This document provides a comprehensive reference for all available fields in WPUF (WordPress User Frontend) post forms, including both free and pro fields. This reference is used by the AI form creation system to understand available field types, properties, and configurations. |
π€ Prompt for AI Agents
In includes/AI/wpuf-post-form-fields-reference.md around lines 1 to 3, add a
clear top-note before the existing content stating that the example field blobs
are schematic only and omit required properties, and that any generated forms
must comply with the full schema specified by the system prompt; keep the
wording short, unambiguous, and prominent (e.g., bold or header), and ensure it
instructs downstream generators to never rely on the minimal examples but to
validate and populate all required properties according to the official schema.
| ### File Upload Options | ||
| - **max_size**: Maximum file size in bytes | ||
| - **allowed_extensions**: Allowed file extensions | ||
| - **max_files**: Maximum number of files | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unify file-size units with the system prompt (KB, not bytes).
This doc says βbytes,β while system-prompt instructs βKBβ (e.g., 1024 = 1MB). Mismatch will cause limits to be misapplied.
Apply:
- **max_size**: Maximum file size in bytes
+ **max_size**: Maximum file size in KB (e.g., 1024 = 1 MB)And:
- **max_size**: Maximum file size in bytes (default: 2MB)
+ **max_size**: Maximum file size in KB (default: 2048 KB = 2 MB)Also applies to: 190-198
π€ Prompt for AI Agents
In includes/AI/wpuf-post-form-fields-reference.md around lines 152-156 (and also
190-198), the file upload option descriptions use "bytes" but the system prompt
standardizes on "KB"; update the text to indicate sizes are in KB (e.g.,
"max_size: Maximum file size in KB (1024 = 1MB)"), and adjust any examples or
labels to KB units, ensuring consistency across both places mentioned.
| ## Pro vs Free Fields | ||
|
|
||
| ### Free Fields | ||
| All basic functionality is available without upgrading: | ||
| - Post fields (title, content, tags, categories) | ||
| - Basic input fields (text, email, textarea, radio, checkbox, dropdown) | ||
| - Layout fields (columns, sections) | ||
| - Basic utility fields (HTML, hidden, image upload, reCAPTCHA) | ||
|
|
||
| ### Pro Fields | ||
| Advanced functionality requires WPUF Pro: | ||
| - User profile fields | ||
| - Social media fields | ||
| - Advanced input types (date, time, file, phone, address) | ||
| - Special fields (ratings, embed, multi-step, etc.) | ||
| - Custom taxonomy support beyond basic categories/tags | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π‘ Verification agent
π§© Analysis chain
Confirm canonical names for βmultiple_selectβ and βcustom_hidden_fieldβ.
If WPUF uses multiselect as a select variant or hidden_field as the slug, rename entries to canonical keys.
π Script executed:
#!/bin/bash
rg -nP -C2 '\bmultiple_select\b|\bcustom_hidden_field\b' --type=php --type=jsLength of output: 129579
π Script executed:
#!/bin/bash
rg -nP 'multiselect' -C2 --type=php --type=jsLength of output: 267704
Normalize canonical keys: decide & apply consistent names for MultiβSelect and Hidden fields
-
multiple_select vs multiselect β codebase uses both: template/field-manager use "multiple_select" (includes/class-field-manager.php, includes/Fields/Form_Field_MultiDropdown.php), while renderers/logic expect "multiselect" (class/render-form.php, wpuf-functions.php, admin template hidden_field sets input_type 'multiselect', assets/js field components use 'multiselect'). Action: pick one canonical input_type/data_type (recommend "multiselect") and align Form_Field_MultiDropdown, field-manager, admin templates, render-form, wpuf-functions, and JS (or add a normalization layer when loading/saving fields).
-
custom_hidden_field β template is "custom_hidden_field" across admin/UI and JS, but some code writes input_type 'hidden' while includes/Fields/Form_Field_Hidden.php sets input_type 'custom_hidden_field'. Action: decide whether input_type should be "hidden" or "custom_hidden_field" (recommend: keep template "custom_hidden_field" and use input_type "hidden" with explicit normalization), then update the field class, admin template, field-manager and JS accordingly.
Files with concrete pointers found: includes/Fields/Form_Field_MultiDropdown.php, includes/Fields/Form_Field_Hidden.php, includes/class-field-manager.php, Admin/Forms/Admin_Template.php (admin/template.php variants), class/render-form.php, wpuf-functions.php, assets/js/wpuf-form-builder-components.js and assets/js-templates/form-components.php.
π€ Prompt for AI Agents
In includes/AI/wpuf-post-form-fields-reference.md around lines 234 to 250: the
repo mixes canonical keys for multi-select and hidden fields causing mismatches
between field classes, manager, renderers, admin templates and JS. Choose and
apply the canonical names (recommend "multiselect" for the multi-select
input_type/data_type and use "hidden" as the input_type with template name
"custom_hidden_field" but normalize on load/save), then update
includes/Fields/Form_Field_MultiDropdown.php,
includes/Fields/Form_Field_Hidden.php, includes/class-field-manager.php,
Admin/Forms/Admin_Template.php (and other admin templates),
class/render-form.php, wpuf-functions.php,
assets/js/wpuf-form-builder-components.js and
assets/js-templates/form-components.php to either use the chosen canonical keys
or add a single normalization layer where fields are loaded/saved that maps
"multiple_select"β"multiselect" and "custom_hidden_field"β"hidden" so all
components consistently read/write the same input_type.
| ### Supported Field Types | ||
| Fields that can be used in conditional logic: | ||
| - `radio_field` | ||
| - `checkbox_field` | ||
| - `dropdown_field` | ||
| - `text_field` | ||
| - `textarea_field` | ||
| - `email_address` | ||
| - `numeric_text_field` | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π‘ Verification agent
π§© Analysis chain
Align conditional-capable field list with real implementation.
Ensure all listed templates support conditions in code to avoid UI mismatches.
π Script executed:
#!/bin/bash
rg -nP -C3 'wpuf_cond|condition_status' --type=phpLength of output: 49863
π Script executed:
#!/bin/bash
# List template definitions and default wpuf_cond values in each field class
for file in includes/Fields/Form_Field_*.php; do
echo "$file"
grep -nP "('template'|wpuf_cond)" -C1 "$file"
echo "----"
doneLength of output: 2943
Update supported conditional field list in documentation
In includes/AI/wpuf-post-form-fields-reference.md (lines 366-375), revise the βSupported Field Typesβ list to exactly match the templates that initialize wpuf_cond (i.e. those that support conditions) and exclude any that donβt. For example, include every template with 'wpuf_cond' => $this->conditionals in Post_Form_Template_Post.php (e.g. radio_field, checkbox_field, dropdown_field, text_field, textarea_field, email_address, numeric_text_field, plus any others defined there) and omit ones with wpuf_cond => null (e.g. html, hidden, section_break, recaptcha, cloudflare_turnstile).
| ### Template Structure | ||
| Each field template follows this structure: | ||
| ```json | ||
| { | ||
| "template": "field_type", | ||
| "label": "Field Label", | ||
| "name": "field_name", | ||
| "required": "yes", | ||
| "help": "Help text", | ||
| "placeholder": "Placeholder text", | ||
| "default": "Default value", | ||
| "css": "custom-class", | ||
| "width": "100%", | ||
| "show_in_post": "yes", | ||
| "hide_field_label": "no", | ||
| "is_meta": "yes" | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Width value conflicts with prompt (βsmall|medium|largeβ vs β100%β).
Use the same enum across docs and generators to avoid parser ambiguity.
Apply:
- "width": "100%",
+ "width": "large",And document allowed values: small | medium | large.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ### Template Structure | |
| Each field template follows this structure: | |
| ```json | |
| { | |
| "template": "field_type", | |
| "label": "Field Label", | |
| "name": "field_name", | |
| "required": "yes", | |
| "help": "Help text", | |
| "placeholder": "Placeholder text", | |
| "default": "Default value", | |
| "css": "custom-class", | |
| "width": "100%", | |
| "show_in_post": "yes", | |
| "hide_field_label": "no", | |
| "is_meta": "yes" | |
| } | |
| ``` | |
| { | |
| "template": "field_type", | |
| "label": "Field Label", | |
| "name": "field_name", | |
| "required": "yes", | |
| "help": "Help text", | |
| "placeholder": "Placeholder text", | |
| "default": "Default value", | |
| "css": "custom-class", | |
| "width": "large", | |
| "show_in_post": "yes", | |
| "hide_field_label": "no", | |
| "is_meta": "yes" | |
| } |
π€ Prompt for AI Agents
In includes/AI/wpuf-post-form-fields-reference.md around lines 482 to 499, the
example template uses a percentage value for "width" ("100%") which conflicts
with the intended enum values; update the example and any surrounding text to
use the canonical enum values (small | medium | large), add a short sentence
documenting the allowed values "small | medium | large", and ensure any
referenced generators/parsers are adjusted to accept and validate those enum
strings instead of percentage formats to avoid ambiguity.
|
Duplicate PR . new PR |
Close issue , issue , issue ,issue
π¨ AI Form Builder β Design & Implementation
π Overview
This PR introduces a groundbreaking AI-powered form builder for WP User Frontend, allowing users to create complex WordPress forms through natural language descriptions. It reimagines the traditional form creation workflow by integrating a conversational, intelligent interface that adapts to user intent and provides real-time refinement options.
The feature is built with a mix of AI orchestration, Vue.js components, modern styling, and secure REST APIs, transforming WP User Frontend into a next-generation, AI-assisted platform.
π Major Additions
π§ AI Infrastructure (+8,500 lines)
π₯οΈ Vue.js Components (+2,825 lines)
π¨ Styling & UI (+8,656 lines)
π Documentation & References (+3,704 lines)
β¨ Key Features
Natural Language Processing
Three-Stage Workflow
Interactive Chat Refinement
Smart Field Mapping
Settings Integration
π§ Technical Enhancements
π Stats
π§ͺ Testing Files
check-ai-status.phpβ AI availability checker.test-ai-context.phpβ AI context validation.test-google-gemini.phpβ Google AI provider tests.setup-google-ai.phpβ Setup and config helper.π― Impact
This feature elevates WP User Frontend into an AI-assisted form creation platform, bridging the gap between beginner-friendly usability and professional-grade customization. Users can now describe forms in plain English and instantly receive production-ready WordPress forms, making form creation faster, smarter, and more accessible.
Summary by CodeRabbit
New Features
Style
Documentation
Chores
Tests