-
Notifications
You must be signed in to change notification settings - Fork 153
feature: n8n integration for post form #1725
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
base: develop
Are you sure you want to change the base?
feature: n8n integration for post form #1725
Conversation
WalkthroughAdds an N8N integration and loader, extends Settings API to emit and sanitize multi-field dependency attributes and adds comprehensive front-end dependency logic, introduces a password-preview field renderer (duplicated), removes vendor-prefixed backdrop-filter CSS usage, and updates POT translations with N8N strings. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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: 8
🧹 Nitpick comments (4)
assets/js/wpuf-form-builder.js (1)
1250-1256: n8n_webhook_url: use URL semantics and basic validation
- Prefer type 'url' and a simple pattern/placeholder to reduce bad inputs (e.g., https://your-n8n-host/webhook/...).
- Confirm the controlling field id 'enable_n8n' matches the actual DOM id used by FormDependencyHandler.
Lib/WeDevs_Settings_API.php (1)
144-145: Good addition of depends_on_value + safe rendering; prefer wp_json_encode
- The new depends_on_value plumbs through correctly and data attributes are allowed in wp_kses. LGTM.
- Minor: use wp_json_encode instead of json_encode for better encoding and consistency in WP.
Also applies to: 177-205, 324-353
includes/Integrations.php (1)
31-38: Simplify error logging.The
print_r()call on Line 37 is redundant since the argument is already a string concatenation. This can be simplified for clarity.Apply this diff:
- \WP_User_Frontend::log( 'integration', print_r( $external_class . ' integration failed', true ) ); + \WP_User_Frontend::log( 'integration', $external_class . ' integration failed' );includes/Integrations/WPUF_N8N_Integration.php (1)
105-139: Consider extracting authentication logic.The authentication switch statement is comprehensive but could benefit from extraction into separate private methods for better maintainability and testability.
Consider refactoring like this:
switch ( $auth_type ) { case 'basic_auth': $this->add_basic_auth_header( $headers, $n8n_settings ); break; case 'header_auth': $this->add_header_auth( $headers, $n8n_settings ); break; case 'jwt_auth': $this->add_jwt_auth_header( $headers, $n8n_settings ); break; }Note: The PHPCS warnings about
base64_encode()on Lines 110, 367, 368, and 371 are false positives. The function is legitimately used for Basic Authentication and JWT token encoding, not code obfuscation.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
Lib/WeDevs_Settings_API.php(5 hunks)assets/js/wpuf-form-builder.js(1 hunks)includes/Integrations.php(1 hunks)includes/Integrations/WPUF_N8N_Integration.php(1 hunks)wpuf-functions.php(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
Lib/WeDevs_Settings_API.php (1)
wpuf-functions.php (1)
wpuf_get_pro_preview_html(4696-4702)
wpuf-functions.php (1)
Lib/WeDevs_Settings_API.php (3)
WeDevs_Settings_API(13-892)get_option(579-588)get_field_description(162-170)
includes/Integrations.php (2)
includes/Integrations/WPUF_N8N_Integration.php (1)
__construct(17-25)wpuf.php (2)
__construct(74-88)log(323-326)
includes/Integrations/WPUF_N8N_Integration.php (2)
includes/Integrations.php (2)
Integrations(10-66)__construct(29-48)wpuf-functions.php (1)
wpuf_is_checkbox_or_toggle_on(5643-5645)
🪛 GitHub Check: Run PHPCS inspection
includes/Integrations/WPUF_N8N_Integration.php
[warning] 371-371:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[warning] 368-368:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[warning] 367-367:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[failure] 367-367:
Expected 1 space after the array opener in a single line array. Found: no spaces
[failure] 365-365:
Closing parenthesis of a multi-line function call must be on a line by itself
[failure] 361-361:
Opening parenthesis of a multi-line function call must be the last content on the line
[warning] 361-361:
json_encode() is discouraged. Use wp_json_encode() instead.
[failure] 359-359:
Closing parenthesis of a multi-line function call must be on a line by itself
[failure] 358-358:
There should be a comma after the last array item in a multi-line array.
[failure] 356-356:
Opening parenthesis of a multi-line function call must be the last content on the line
[warning] 356-356:
json_encode() is discouraged. Use wp_json_encode() instead.
[failure] 272-272:
There should be a comma after the last array item in a multi-line array.
[failure] 239-239:
Multi-line function call not indented correctly; expected 24 spaces but found 28
[warning] 110-110:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[failure] 91-91:
Array keys must be surrounded by spaces unless they contain a string or an integer.
[failure] 88-88:
The shorthand list syntax "[]" to destructure arrays is not available in PHP 7.0 or earlier.
🪛 PHPMD (2.15.0)
includes/Integrations/WPUF_N8N_Integration.php
88-88: Avoid unused local variables such as '$multi_repeated'. (undefined)
(UnusedLocalVariable)
88-88: Avoid unused local variables such as '$files'. (undefined)
(UnusedLocalVariable)
142-142: Avoid unused local variables such as '$response'. (undefined)
(UnusedLocalVariable)
🔇 Additional comments (8)
wpuf-functions.php (1)
4041-4085: No duplicate function declarations detected—verification confirms safe declaration.Ripgrep search found only one definition of
wpuf_settings_password_previewat line 4047 in wpuf-functions.php. No fatal redeclaration issues exist.includes/Integrations.php (2)
26-26: LGTM! N8N integration entry added.The integration registry entry is correctly formatted and consistent with existing integrations.
39-39: LGTM! Correct use of elseif.The change from
iftoelseifcorrectly prevents the N8N integration from being processed twice.includes/Integrations/WPUF_N8N_Integration.php (5)
17-25: LGTM! Constructor properly registers hooks.The constructor correctly registers filters for settings UI and an action hook for post submission with appropriate priority.
39-58: LGTM! Proper validation and sanitization.The method correctly validates that N8N is enabled, the webhook URL exists, and the post is valid before proceeding. The use of
esc_url_raw()for URL sanitization is appropriate.
161-169: LGTM! Settings section added correctly.The method properly adds the N8N settings section to the configuration.
305-317: LGTM! Menu title with icon added correctly.The method properly adds N8N to the form builder menu with a custom SVG icon.
328-344: LGTM! Form settings configured correctly.The method properly adds form-level N8N configuration options. The webhook URL field will be conditionally displayed based on the enable_n8n toggle, as configured in the JavaScript dependencies.
| [ $meta_key_value, $multi_repeated, $files ] = self::prepare_meta_fields( $meta_vars ); | ||
|
|
||
| foreach ( $meta_key_value as $meta_key => $meta_value ) { | ||
| $post_data[$meta_key] = $meta_value; | ||
| } |
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.
🧩 Analysis chain
Address unused variables and potential PHP compatibility issue.
Line 88 has two concerns:
- Array destructuring syntax
[ $meta_key_value, $multi_repeated, $files ]requires PHP 7.1+, which may cause compatibility issues if the plugin supports PHP 7.0. - Variables
$multi_repeatedand$filesare destructured but never used.
Consider one of these solutions:
Solution 1: Only destructure what's needed
- [ $meta_key_value, $multi_repeated, $files ] = self::prepare_meta_fields( $meta_vars );
+ [ $meta_key_value ] = self::prepare_meta_fields( $meta_vars );Solution 2: Use traditional assignment if PHP 7.0 support is required
- [ $meta_key_value, $multi_repeated, $files ] = self::prepare_meta_fields( $meta_vars );
+ $prepared = self::prepare_meta_fields( $meta_vars );
+ $meta_key_value = $prepared[0];Please verify the minimum PHP version requirement for this plugin to determine the appropriate solution.
🏁 Script executed:
#!/bin/bash
# Find PHP version requirement in composer.json or other config files
echo "=== Checking PHP version requirement ==="
if [ -f "composer.json" ]; then
echo "From composer.json:"
jq -r '.require.php // "Not specified"' composer.json
fi
if [ -f "readme.txt" ]; then
echo "From readme.txt (PHP requirement lines):"
grep -i "php" readme.txt | head -5
fi
if [ -f "package.json" ]; then
echo "From package.json:"
jq -r '.engines.php // "Not specified"' package.json 2>/dev/null || echo "N/A"
fiLength of output: 409
🏁 Script executed:
#!/bin/bash
# Examine the specific file and lines in question
echo "=== Examining WPUF_N8N_Integration.php around lines 88-92 ==="
if [ -f "includes/Integrations/WPUF_N8N_Integration.php" ]; then
echo "Lines 80-100:"
sed -n '80,100p' "includes/Integrations/WPUF_N8N_Integration.php"
else
echo "File not found at includes/Integrations/WPUF_N8N_Integration.php"
find . -name "WPUF_N8N_Integration.php" -type f 2>/dev/null
fiLength of output: 837
🏁 Script executed:
#!/bin/bash
# Search for usage of $multi_repeated and $files variables in the file
echo "=== Searching for $multi_repeated usage ==="
rg -n '\$multi_repeated' "includes/Integrations/" -C 3 || echo "No matches for \$multi_repeated"
echo ""
echo "=== Searching for $files usage (excluding function parameter context) ==="
rg -n '\$files' "includes/Integrations/" -C 3 || echo "No matches for \$files"Length of output: 1395
🏁 Script executed:
#!/bin/bash
# Find the prepare_meta_fields method to understand return structure
echo "=== Searching for prepare_meta_fields method definition ==="
rg -n 'prepare_meta_fields' "includes/Integrations/" -A 15 -B 2 | head -50Length of output: 1653
🏁 Script executed:
#!/bin/bash
# Search for prepare_meta_fields method definition more broadly (may be in parent class or trait)
echo "=== Searching for prepare_meta_fields method definition anywhere ==="
rg -n 'function prepare_meta_fields|public.*prepare_meta_fields|private.*prepare_meta_fields' -A 20 || echo "No explicit function definition found"
echo ""
echo "=== Checking for class definition and inheritance in WPUF_N8N_Integration.php ==="
head -50 "includes/Integrations/WPUF_N8N_Integration.php"Length of output: 7965
Fix PHP 7.1+ incompatibility and remove unused variables in line 88.
This file uses FieldableTrait which contains prepare_meta_fields(). The plugin supports PHP 5.6+ (per readme.txt and composer.json), but line 88 uses array destructuring syntax [ ... ] which requires PHP 7.1+. This is a breaking change. Additionally, $multi_repeated and $files are destructured but never used.
Use only what's needed:
- [ $meta_key_value, $multi_repeated, $files ] = self::prepare_meta_fields( $meta_vars );
+ [ $meta_key_value ] = self::prepare_meta_fields( $meta_vars );Or for PHP 5.6 compatibility:
- [ $meta_key_value, $multi_repeated, $files ] = self::prepare_meta_fields( $meta_vars );
+ $prepared = self::prepare_meta_fields( $meta_vars );
+ $meta_key_value = $prepared[0];📝 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.
| [ $meta_key_value, $multi_repeated, $files ] = self::prepare_meta_fields( $meta_vars ); | |
| foreach ( $meta_key_value as $meta_key => $meta_value ) { | |
| $post_data[$meta_key] = $meta_value; | |
| } | |
| $prepared = self::prepare_meta_fields( $meta_vars ); | |
| $meta_key_value = $prepared[0]; | |
| foreach ( $meta_key_value as $meta_key => $meta_value ) { | |
| $post_data[$meta_key] = $meta_value; | |
| } |
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection
[failure] 91-91:
Array keys must be surrounded by spaces unless they contain a string or an integer.
[failure] 88-88:
The shorthand list syntax "[]" to destructure arrays is not available in PHP 7.0 or earlier.
🪛 PHPMD (2.15.0)
88-88: Avoid unused local variables such as '$multi_repeated'. (undefined)
(UnusedLocalVariable)
88-88: Avoid unused local variables such as '$files'. (undefined)
(UnusedLocalVariable)
🤖 Prompt for AI Agents
In includes/Integrations/WPUF_N8N_Integration.php around lines 88 to 92, the
code uses PHP 7.1 array destructuring and binds unused variables; replace the
destructuring with PHP 5.6-compatible handling by calling
self::prepare_meta_fields($meta_vars) into a single variable and then extract
only the first element (the $meta_key_value) for use, dropping $multi_repeated
and $files; update the subsequent loop to use that extracted $meta_key_value.
| $response = wp_remote_post( | ||
| $n8n_webhook_url, [ | ||
| 'method' => 'POST', | ||
| 'timeout' => 30, | ||
| 'headers' => $headers, | ||
| 'body' => wp_json_encode( $post_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.
Critical: Add error handling and response validation.
The webhook request response is captured but never checked for errors. Failed webhook deliveries will fail silently, making it impossible for users to debug integration issues.
Add error handling and logging:
// Send data to N8N webhook
$response = wp_remote_post(
$n8n_webhook_url, [
'method' => 'POST',
'timeout' => 30,
'headers' => $headers,
'body' => wp_json_encode( $post_data ),
]
);
+
+ // Check for errors
+ if ( is_wp_error( $response ) ) {
+ \WP_User_Frontend::log( 'n8n', 'Webhook delivery failed: ' . $response->get_error_message() );
+ return;
+ }
+
+ $response_code = wp_remote_retrieve_response_code( $response );
+ if ( $response_code < 200 || $response_code >= 300 ) {
+ $response_body = wp_remote_retrieve_body( $response );
+ \WP_User_Frontend::log( 'n8n', sprintf( 'Webhook returned %d: %s', $response_code, $response_body ) );
+ }
}🧰 Tools
🪛 PHPMD (2.15.0)
142-142: Avoid unused local variables such as '$response'. (undefined)
(UnusedLocalVariable)
🤖 Prompt for AI Agents
In includes/Integrations/WPUF_N8N_Integration.php around lines 142 to 150, the
wp_remote_post response is not validated or error-checked; update the code to
check for WP_Error (is_wp_error), verify HTTP status is 2xx, parse/validate the
response body (json_decode and check for expected keys), and log failures.
Specifically: after wp_remote_post, if is_wp_error($response) log the error
message and return/handle accordingly; otherwise get the status with
wp_remote_retrieve_response_code and if it's not between 200 and 299 log the
status, response body and headers; attempt to json_decode the body and validate
expected fields, logging malformed JSON; use a consistent logger (error_log or
WordPress logger) and include contextual info (webhook URL, payload id/user) in
logs to aid debugging.
| public function add_n8n_settings_fields( $fields ) { | ||
| $settings = [ | ||
| 'n8n' => [ | ||
| [ | ||
| 'name' => 'authentication_type', | ||
| 'label' => __( 'Authentication Type', 'wp-user-frontend' ), | ||
| 'desc' => __( | ||
| 'Select the authentication type for the N8N integration.', | ||
| 'wp-user-frontend' | ||
| ), | ||
| 'type' => 'select', | ||
| 'default' => 'none', | ||
| 'options' => [ | ||
| 'none' => __( 'None', 'wp-user-frontend' ), | ||
| 'basic_auth' => __( 'Basic Auth', 'wp-user-frontend' ), | ||
| 'header_auth' => __( 'Header Auth', 'wp-user-frontend' ), | ||
| 'jwt_auth' => __( 'JWT Auth', 'wp-user-frontend' ), | ||
| ], | ||
| ], | ||
| [ | ||
| 'name' => 'basic_auth_username', | ||
| 'label' => __( 'Basic Auth Username', 'wp-user-frontend' ), | ||
| 'desc' => __( | ||
| 'Enter the username for the Basic Auth authentication.', | ||
| 'wp-user-frontend' | ||
| ), | ||
| 'type' => 'text', | ||
| 'depends_on' => 'authentication_type', | ||
| 'depends_on_value' => 'basic_auth', | ||
| ], | ||
| [ | ||
| 'name' => 'basic_auth_password', | ||
| 'label' => __( 'Basic Auth Password', 'wp-user-frontend' ), | ||
| 'desc' => __( | ||
| 'Enter the password for the Basic Auth authentication.', | ||
| 'wp-user-frontend' | ||
| ), | ||
| 'type' => 'text', | ||
| 'callback' => 'wpuf_settings_password_preview', | ||
| 'depends_on' => 'authentication_type', | ||
| 'depends_on_value' => 'basic_auth', | ||
| ], | ||
| [ | ||
| 'name' => 'header_auth_name', | ||
| 'label' => __( 'Name', 'wp-user-frontend' ), | ||
| 'desc' => __( | ||
| 'Enter the name for the Header Auth authentication.', | ||
| 'wp-user-frontend' | ||
| ), | ||
| 'type' => 'text', | ||
| 'depends_on' => 'authentication_type', | ||
| 'depends_on_value' => 'header_auth', | ||
| ], | ||
| [ | ||
| 'name' => 'header_auth_value', | ||
| 'label' => __( 'Header Auth Value', 'wp-user-frontend' ), | ||
| 'desc' => __( | ||
| 'Enter the value for the Header Auth authentication.', | ||
| 'wp-user-frontend' | ||
| ), | ||
| 'type' => 'text', | ||
| 'callback' => 'wpuf_settings_password_preview', | ||
| 'depends_on' => 'authentication_type', | ||
| 'depends_on_value' => 'header_auth', | ||
| ], | ||
| [ | ||
| 'name' => 'jwt_key_type', | ||
| 'label' => __( 'JWT Key Type', 'wp-user-frontend' ), | ||
| 'desc' => __( | ||
| 'Select the type of JWT key for the JWT Auth authentication.', | ||
| 'wp-user-frontend' | ||
| ), | ||
| 'type' => 'select', | ||
| 'default' => 'passphrase', | ||
| 'options' => [ | ||
| 'passphrase' => __( 'Passphrase', 'wp-user-frontend' ), | ||
| 'pem_key' => __( 'PEM Key', 'wp-user-frontend' ), | ||
| ], | ||
| 'depends_on' => 'authentication_type', | ||
| 'depends_on_value' => 'jwt_auth', | ||
| ], | ||
| [ | ||
| 'name' => 'jwt_key_passphrase', | ||
| 'label' => __( 'Passphrase', 'wp-user-frontend' ), | ||
| 'desc' => __( | ||
| 'Enter the passphrase for the JWT Auth authentication.', | ||
| 'wp-user-frontend' | ||
| ), | ||
| 'type' => 'text', | ||
| 'callback' => 'wpuf_settings_password_preview', | ||
| 'depends_on' => [ | ||
| 'authentication_type' => 'jwt_auth', | ||
| 'jwt_key_type' => 'passphrase' | ||
| ], | ||
| ], | ||
| [ | ||
| 'name' => 'jwt_key_pem_key', | ||
| 'label' => __( 'PEM Key', 'wp-user-frontend' ), | ||
| 'desc' => __( | ||
| 'Enter the PEM key for the JWT Auth authentication.', | ||
| 'wp-user-frontend' | ||
| ), | ||
| 'type' => 'text', | ||
| 'callback' => 'wpuf_settings_password_preview', | ||
| 'depends_on' => [ | ||
| 'authentication_type' => 'jwt_auth', | ||
| 'jwt_key_type' => 'pem_key', | ||
| ], | ||
| ], | ||
| ], | ||
| ]; | ||
|
|
||
| return array_merge( $fields, $settings ); | ||
| } |
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.
Consider encrypting sensitive authentication credentials.
Authentication credentials (passwords, JWT secrets, API keys) are currently stored in plain text in the wp_options table. This poses a security risk if the database is compromised.
Consider implementing encryption for sensitive fields:
- Use WordPress's built-in encryption functions or a library like
sodium(available in PHP 7.2+) - Encrypt values before storing with
update_option() - Decrypt when retrieving with
get_option()
Alternatively, document this security consideration and recommend that users:
- Store N8N webhooks on a secure network
- Use webhook URL query parameters for authentication instead of storing credentials
- Regularly rotate credentials
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection
[failure] 272-272:
There should be a comma after the last array item in a multi-line array.
[failure] 239-239:
Multi-line function call not indented correctly; expected 24 spaces but found 28
Fix coding standards violations.
The PHPCS static analysis identified formatting issues that should be corrected for consistency with WordPress coding standards.
Apply these fixes:
'desc' => __(
'Enter the value for the Header Auth authentication.',
- 'wp-user-frontend'
- ),
+ 'wp-user-frontend'
+ ), 'depends_on' => [
'authentication_type' => 'jwt_auth',
- 'jwt_key_type' => 'passphrase'
+ 'jwt_key_type' => 'passphrase',
], 'depends_on' => [
'authentication_type' => 'jwt_auth',
'jwt_key_type' => 'pem_key',
],📝 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 add_n8n_settings_fields( $fields ) { | |
| $settings = [ | |
| 'n8n' => [ | |
| [ | |
| 'name' => 'authentication_type', | |
| 'label' => __( 'Authentication Type', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Select the authentication type for the N8N integration.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'select', | |
| 'default' => 'none', | |
| 'options' => [ | |
| 'none' => __( 'None', 'wp-user-frontend' ), | |
| 'basic_auth' => __( 'Basic Auth', 'wp-user-frontend' ), | |
| 'header_auth' => __( 'Header Auth', 'wp-user-frontend' ), | |
| 'jwt_auth' => __( 'JWT Auth', 'wp-user-frontend' ), | |
| ], | |
| ], | |
| [ | |
| 'name' => 'basic_auth_username', | |
| 'label' => __( 'Basic Auth Username', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the username for the Basic Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'basic_auth', | |
| ], | |
| [ | |
| 'name' => 'basic_auth_password', | |
| 'label' => __( 'Basic Auth Password', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the password for the Basic Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'callback' => 'wpuf_settings_password_preview', | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'basic_auth', | |
| ], | |
| [ | |
| 'name' => 'header_auth_name', | |
| 'label' => __( 'Name', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the name for the Header Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'header_auth', | |
| ], | |
| [ | |
| 'name' => 'header_auth_value', | |
| 'label' => __( 'Header Auth Value', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the value for the Header Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'callback' => 'wpuf_settings_password_preview', | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'header_auth', | |
| ], | |
| [ | |
| 'name' => 'jwt_key_type', | |
| 'label' => __( 'JWT Key Type', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Select the type of JWT key for the JWT Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'select', | |
| 'default' => 'passphrase', | |
| 'options' => [ | |
| 'passphrase' => __( 'Passphrase', 'wp-user-frontend' ), | |
| 'pem_key' => __( 'PEM Key', 'wp-user-frontend' ), | |
| ], | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'jwt_auth', | |
| ], | |
| [ | |
| 'name' => 'jwt_key_passphrase', | |
| 'label' => __( 'Passphrase', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the passphrase for the JWT Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'callback' => 'wpuf_settings_password_preview', | |
| 'depends_on' => [ | |
| 'authentication_type' => 'jwt_auth', | |
| 'jwt_key_type' => 'passphrase' | |
| ], | |
| ], | |
| [ | |
| 'name' => 'jwt_key_pem_key', | |
| 'label' => __( 'PEM Key', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the PEM key for the JWT Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'callback' => 'wpuf_settings_password_preview', | |
| 'depends_on' => [ | |
| 'authentication_type' => 'jwt_auth', | |
| 'jwt_key_type' => 'pem_key', | |
| ], | |
| ], | |
| ], | |
| ]; | |
| return array_merge( $fields, $settings ); | |
| } | |
| public function add_n8n_settings_fields( $fields ) { | |
| $settings = [ | |
| 'n8n' => [ | |
| [ | |
| 'name' => 'authentication_type', | |
| 'label' => __( 'Authentication Type', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Select the authentication type for the N8N integration.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'select', | |
| 'default' => 'none', | |
| 'options' => [ | |
| 'none' => __( 'None', 'wp-user-frontend' ), | |
| 'basic_auth' => __( 'Basic Auth', 'wp-user-frontend' ), | |
| 'header_auth' => __( 'Header Auth', 'wp-user-frontend' ), | |
| 'jwt_auth' => __( 'JWT Auth', 'wp-user-frontend' ), | |
| ], | |
| ], | |
| [ | |
| 'name' => 'basic_auth_username', | |
| 'label' => __( 'Basic Auth Username', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the username for the Basic Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'basic_auth', | |
| ], | |
| [ | |
| 'name' => 'basic_auth_password', | |
| 'label' => __( 'Basic Auth Password', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the password for the Basic Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'callback' => 'wpuf_settings_password_preview', | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'basic_auth', | |
| ], | |
| [ | |
| 'name' => 'header_auth_name', | |
| 'label' => __( 'Name', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the name for the Header Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'header_auth', | |
| ], | |
| [ | |
| 'name' => 'header_auth_value', | |
| 'label' => __( 'Header Auth Value', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the value for the Header Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'callback' => 'wpuf_settings_password_preview', | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'header_auth', | |
| ], | |
| [ | |
| 'name' => 'jwt_key_type', | |
| 'label' => __( 'JWT Key Type', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Select the type of JWT key for the JWT Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'select', | |
| 'default' => 'passphrase', | |
| 'options' => [ | |
| 'passphrase' => __( 'Passphrase', 'wp-user-frontend' ), | |
| 'pem_key' => __( 'PEM Key', 'wp-user-frontend' ), | |
| ], | |
| 'depends_on' => 'authentication_type', | |
| 'depends_on_value' => 'jwt_auth', | |
| ], | |
| [ | |
| 'name' => 'jwt_key_passphrase', | |
| 'label' => __( 'Passphrase', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the passphrase for the JWT Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'callback' => 'wpuf_settings_password_preview', | |
| 'depends_on' => [ | |
| 'authentication_type' => 'jwt_auth', | |
| 'jwt_key_type' => 'passphrase', | |
| ], | |
| ], | |
| [ | |
| 'name' => 'jwt_key_pem_key', | |
| 'label' => __( 'PEM Key', 'wp-user-frontend' ), | |
| 'desc' => __( | |
| 'Enter the PEM key for the JWT Auth authentication.', | |
| 'wp-user-frontend' | |
| ), | |
| 'type' => 'text', | |
| 'callback' => 'wpuf_settings_password_preview', | |
| 'depends_on' => [ | |
| 'authentication_type' => 'jwt_auth', | |
| 'jwt_key_type' => 'pem_key', | |
| ], | |
| ], | |
| ], | |
| ]; | |
| return array_merge( $fields, $settings ); | |
| } |
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection
[failure] 272-272:
There should be a comma after the last array item in a multi-line array.
[failure] 239-239:
Multi-line function call not indented correctly; expected 24 spaces but found 28
🤖 Prompt for AI Agents
in includes/Integrations/WPUF_N8N_Integration.php around lines 180 to 293, PHPCS
flagged WordPress coding-standards formatting issues; adjust indentation and
spacing to match WP standards: use 1 tab per indent level, align array
keys/values with single space around =>, ensure multiline arrays use trailing
commas on last item, wrap long function argument lists and array entries to
respect 80-120 char limits, place closing array brackets and parentheses on
their own lines and ensure consistent blank lines between blocks; run phpcs
--standard=WordPress and apply its suggested fixes to verify.
| private function create_jwt_token( $secret_key ) { | ||
| // Simple JWT implementation - you might want to use a proper JWT library | ||
| $header = json_encode( [ | ||
| 'typ' => 'JWT', | ||
| 'alg' => 'HS256' | ||
| ] ); | ||
|
|
||
| $payload = json_encode( [ | ||
| 'iat' => time(), | ||
| 'exp' => time() + 3600, // 1 hour expiration | ||
| 'iss' => get_site_url(), | ||
| ] ); | ||
|
|
||
| $base64_header = str_replace( ['+', '/', '='], ['-', '_', ''], base64_encode( $header ) ); | ||
| $base64_payload = str_replace( ['+', '/', '='], ['-', '_', ''], base64_encode( $payload ) ); | ||
|
|
||
| $signature = hash_hmac( 'sha256', $base64_header . '.' . $base64_payload, $secret_key, true ); | ||
| $base64_signature = str_replace( ['+', '/', '='], ['-', '_', ''], base64_encode( $signature ) ); | ||
|
|
||
| return $base64_header . '.' . $base64_payload . '.' . $base64_signature; | ||
| } |
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 | 🟠 Major
Use a proper JWT library instead of custom implementation.
The custom JWT implementation works for basic cases but lacks important features and may have security implications. The comment on Line 355 acknowledges this concern.
Consider using a well-tested JWT library like firebase/php-jwt:
composer require firebase/php-jwtThen replace the custom implementation:
use Firebase\JWT\JWT;
private function create_jwt_token( $secret_key ) {
$payload = [
'iat' => time(),
'exp' => time() + 3600,
'iss' => get_site_url(),
];
try {
return JWT::encode( $payload, $secret_key, 'HS256' );
} catch ( \Exception $e ) {
\WP_User_Frontend::log( 'n8n', 'JWT generation failed: ' . $e->getMessage() );
return false;
}
}This provides:
- Proper JWT standard compliance
- Better error handling
- Support for various algorithms
- Security updates from the library maintainers
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection
[warning] 371-371:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[warning] 368-368:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[warning] 367-367:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[failure] 367-367:
Expected 1 space after the array opener in a single line array. Found: no spaces
[failure] 365-365:
Closing parenthesis of a multi-line function call must be on a line by itself
[failure] 361-361:
Opening parenthesis of a multi-line function call must be the last content on the line
[warning] 361-361:
json_encode() is discouraged. Use wp_json_encode() instead.
[failure] 359-359:
Closing parenthesis of a multi-line function call must be on a line by itself
[failure] 358-358:
There should be a comma after the last array item in a multi-line array.
[failure] 356-356:
Opening parenthesis of a multi-line function call must be the last content on the line
[warning] 356-356:
json_encode() is discouraged. Use wp_json_encode() instead.
🤖 Prompt for AI Agents
In includes/Integrations/WPUF_N8N_Integration.php around lines 354 to 374,
replace the custom JWT creation with a call to a vetted library (e.g.,
firebase/php-jwt): add the library via composer, import the JWT class at top of
the file, and update create_jwt_token to build the payload and call
JWT::encode($payload, $secret_key, 'HS256'); wrap the encode call in a try/catch
to log exceptions (WP_User_Frontend::log or process logger) and return false on
failure; ensure the function’s return semantics reflect string on success or
false on error.
Fix coding standards violations in JWT method.
WordPress coding standards require using wp_json_encode() instead of json_encode() and proper formatting for multi-line function calls.
Apply these fixes:
- $header = json_encode( [
+ $header = wp_json_encode(
+ [
'typ' => 'JWT',
- 'alg' => 'HS256'
- ] );
+ 'alg' => 'HS256',
+ ]
+ );
- $payload = json_encode( [
+ $payload = wp_json_encode(
+ [
'iat' => time(),
'exp' => time() + 3600, // 1 hour expiration
'iss' => get_site_url(),
- ] );
+ ]
+ );
- $base64_header = str_replace( ['+', '/', '='], ['-', '_', ''], base64_encode( $header ) );
+ $base64_header = str_replace( [ '+', '/', '=' ], [ '-', '_', '' ], base64_encode( $header ) );Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection
[warning] 371-371:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[warning] 368-368:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[warning] 367-367:
base64_encode() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.
[failure] 367-367:
Expected 1 space after the array opener in a single line array. Found: no spaces
[failure] 365-365:
Closing parenthesis of a multi-line function call must be on a line by itself
[failure] 361-361:
Opening parenthesis of a multi-line function call must be the last content on the line
[warning] 361-361:
json_encode() is discouraged. Use wp_json_encode() instead.
[failure] 359-359:
Closing parenthesis of a multi-line function call must be on a line by itself
[failure] 358-358:
There should be a comma after the last array item in a multi-line array.
[failure] 356-356:
Opening parenthesis of a multi-line function call must be the last content on the line
[warning] 356-356:
json_encode() is discouraged. Use wp_json_encode() instead.
🤖 Prompt for AI Agents
In includes/Integrations/WPUF_N8N_Integration.php around lines 354 to 374, the
create_jwt_token method uses json_encode and has compact multi-line
array/function calls that violate WP coding standards; replace json_encode()
with wp_json_encode() for both $header and $payload and reformat the multi-line
array and function calls to follow WP formatting (each array element and
function argument on its own line with proper indentation and trailing commas
where appropriate) while keeping the same logic and return value.
|
Input box for turnstile site key and secret key is not visible in settings page. @sapayth vai
|
…tion_for_post_form
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: 1
♻️ Duplicate comments (1)
Lib/WeDevs_Settings_API.php (1)
873-873: Dependency logic breaks for checkboxes and radios.The comparison
$depends_on.val() !== expected_valueon lines 873 and 924 fails for checkboxes and radios. For checkboxes,.val()returns the value attribute ("on" by default) regardless of checked state. For radios, you must select the checked radio first.This issue was previously flagged with a detailed fix that has not been implemented.
Apply the patch from the previous review to handle checkbox and radio types correctly by checking the checked state for checkboxes and selecting the checked radio for radio groups before comparing values.
Also applies to: 924-924
🧹 Nitpick comments (2)
Lib/WeDevs_Settings_API.php (2)
847-864: Extract duplicated selector logic into a helper function.The selector fallback logic (trying multiple patterns to find the dependency field) is duplicated in both the initial dependency check and the
checkAllDependencies()function. This makes maintenance harder and increases code size.Consider extracting this logic into a helper function:
+ // Helper function to find dependency field + function findDependencyField(field_name) { + var $field = $("input[id*='"+ field_name +"'], select[id*='"+ field_name +"']"); + if ($field.length === 0) { + $field = $("input[name*='["+ field_name +"]'], select[name*='["+ field_name +"]']"); + } + if ($field.length === 0) { + $field = $("input[id*='["+ field_name +"]'], select[id*='["+ field_name +"]']"); + } + if ($field.length === 0) { + $field = $("input[id*='"+ field_name +"'], select[id*='"+ field_name +"']"); + } + return $field; + }Then use
findDependencyField(field_name)in both locations instead of repeating the selector logic.Also applies to: 907-922
902-936: Function defined inside loop creates unnecessary closures.The
checkAllDependencies()function is defined inside the.each()loop, creating a new function instance for each field with dependencies. While this works due to closures capturing$thisanddependencies, it's inefficient and could impact performance with many dependent fields.Consider refactoring to avoid creating multiple function instances. One approach is to store the dependencies and target element as data attributes or in a map, then use a single shared function.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
Lib/WeDevs_Settings_API.php(5 hunks)assets/js/wpuf-form-builder.js(1 hunks)wpuf-functions.php(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- assets/js/wpuf-form-builder.js
- wpuf-functions.php
🧰 Additional context used
🧬 Code graph analysis (1)
Lib/WeDevs_Settings_API.php (1)
wpuf-functions.php (1)
wpuf_get_pro_preview_html(4698-4704)
| $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) ); | ||
| $disabled = ! empty( $args['is_pro_preview'] ) && $args['is_pro_preview'] ? 'disabled' : ''; | ||
| $size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular'; | ||
| $depends_on = ! empty( $args['depends_on'] ) ? $args['depends_on'] : ''; |
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 'placeholder' attribute in wp_kses allowed attributes.
The placeholder attribute is used in the sprintf on line 195 (set on line 181), but it's not included in the wp_kses allowed attributes list on line 204. This will cause the placeholder to be stripped from the output.
Apply this diff to add the missing attribute:
- echo wp_kses( $html, array('input' => ['type' => [],'class' => [],'id' => [],'name' => [],'value' => [],'disabled' => [],'data-depends-on' => [],'data-depends-on-value' => []], 'p' => ['class' => []], 'div' => ['class' => []], 'a' => ['href' => [],'target' => [],'class' => []], 'span' => ['class' => []], 'svg' => ['width' => [],'height' => [],'viewBox' => [],'fill' => [],'xmlns' => [],],));
+ echo wp_kses( $html, array('input' => ['type' => [],'class' => [],'id' => [],'name' => [],'value' => [],'placeholder' => [],'disabled' => [],'data-depends-on' => [],'data-depends-on-value' => []], 'p' => ['class' => []], 'div' => ['class' => []], 'a' => ['href' => [],'target' => [],'class' => []], 'span' => ['class' => []], 'svg' => ['width' => [],'height' => [],'viewBox' => [],'fill' => [],'xmlns' => [],],));📝 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 wp_kses( $html, array('input' => ['type' => [],'class' => [],'id' => [],'name' => [],'value' => [],'disabled' => [],'data-depends-on' => [],'data-depends-on-value' => []], 'p' => ['class' => []], 'div' => ['class' => []], 'a' => ['href' => [],'target' => [],'class' => []], 'span' => ['class' => []], 'svg' => ['width' => [],'height' => [],'viewBox' => [],'fill' => [],'xmlns' => [],],)); | |
| echo wp_kses( $html, array('input' => ['type' => [],'class' => [],'id' => [],'name' => [],'value' => [],'placeholder' => [],'disabled' => [],'data-depends-on' => [],'data-depends-on-value' => []], 'p' => ['class' => []], 'div' => ['class' => []], 'a' => ['href' => [],'target' => [],'class' => []], 'span' => ['class' => []], 'svg' => ['width' => [],'height' => [],'viewBox' => [],'fill' => [],'xmlns' => [],],)); |
🤖 Prompt for AI Agents
In Lib/WeDevs_Settings_API.php around line 204, the wp_kses allowed-attributes
list for the input tag is missing the 'placeholder' attribute causing
placeholder text (set earlier) to be stripped; update the allowed attributes
array for 'input' to include 'placeholder' => [] so wp_kses will preserve
placeholder values in the rendered HTML.
please have a look now @Rubaiyat-E-Mohammad bhai |
Fix field visibility logic for checkbox dependencies. When a checkbox has an empty expected_value, dependent fields now correctly show when the checkbox is checked (instead of when unchecked). This fixes the issue where fields like 'Turnstile Site Key' and 'Turnstile Secret Key' were hiding when 'Enable Turnstile' checkbox was checked, when they should have been visible. The fix applies proper checkbox state detection using :checked selector and handles both empty and non-empty expected values appropriately. Also includes CSS cleanup (removing duplicate webkit prefixes) and language file updates.
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
languages/wp-user-frontend.pot (1)
3040-3045: Fix awkward help text grammarImprove clarity of “If selected a form template …” string.
Apply in source (then regenerate POT):
- "If selected a form template, it will try to execute that integration options when new post created and updated." + "If a form template is selected, its integration options will run when a post is created or updated."
♻️ Duplicate comments (1)
Lib/WeDevs_Settings_API.php (1)
184-204: Missing 'placeholder' attribute in wp_kses (duplicate issue).The
placeholderattribute is used in the sprintf on line 195 (set on line 181), but it's still not included in the wp_kses allowed attributes list on line 204. This will cause the placeholder to be stripped from the output.Apply this diff to add the missing attribute:
- echo wp_kses( $html, array('input' => ['type' => [],'class' => [],'id' => [],'name' => [],'value' => [],'disabled' => [],'data-depends-on' => [],'data-depends-on-value' => []], 'p' => ['class' => []], 'div' => ['class' => []], 'a' => ['href' => [],'target' => [],'class' => []], 'span' => ['class' => []], 'svg' => ['width' => [],'height' => [],'viewBox' => [],'fill' => [],'xmlns' => [],],)); + echo wp_kses( $html, array('input' => ['type' => [],'class' => [],'id' => [],'name' => [],'value' => [],'placeholder' => [],'disabled' => [],'data-depends-on' => [],'data-depends-on-value' => []], 'p' => ['class' => []], 'div' => ['class' => []], 'a' => ['href' => [],'target' => [],'class' => []], 'span' => ['class' => []], 'svg' => ['width' => [],'height' => [],'viewBox' => [],'fill' => [],'xmlns' => [],],));
🧹 Nitpick comments (1)
Lib/WeDevs_Settings_API.php (1)
846-868: Significant code duplication in dependency field selectors.The selector logic for finding dependency fields is duplicated between the initial check (lines 846-868) and the
checkAllDependencies()function (lines 921-938). This makes the code harder to maintain.Consider extracting the selector logic into a helper function:
+ // Helper function to find dependency field + function findDependencyField(field_name) { + var $field = $("input[id*='"+ field_name +"'], select[id*='"+ field_name +"']"); + if ($field.length === 0) { + $field = $("input[name*='["+ field_name +"]'], select[name*='["+ field_name +"]']"); + } + if ($field.length === 0) { + $field = $("input[id*='["+ field_name +"]'], select[id*='["+ field_name +"]']"); + } + if ($field.length === 0) { + $field = $("input[id*='"+ field_name +"'], select[id*='"+ field_name +"']"); + } + return $field; + }Then replace both instances with calls to
findDependencyField(field_name).Also applies to: 921-938
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
Lib/WeDevs_Settings_API.php(5 hunks)assets/css/admin/form-builder.css(2 hunks)assets/css/ai-form-builder.css(2 hunks)languages/wp-user-frontend.pot(30 hunks)
✅ Files skipped from review due to trivial changes (1)
- assets/css/ai-form-builder.css
🧰 Additional context used
🧬 Code graph analysis (1)
Lib/WeDevs_Settings_API.php (1)
wpuf-functions.php (1)
wpuf_get_pro_preview_html(4698-4704)
🔇 Additional comments (5)
assets/css/admin/form-builder.css (1)
3025-3025: ✓ CSS Modernization: Good use of standardbackdrop-filterproperty.The changes correctly replace vendor-prefixed CSS with the modern standard
backdrop-filterproperty. Both occurrences (lines 3025 and 3045) in the.wpuf-glassand.wpuf-glass.wpuf-btn-activerules now use the standard property, which is well-supported across modern browsers and aligns with CSS standards.No behavioral changes; this is a clean upgrade from vendor prefixes to standardized CSS.
Also applies to: 3045-3045
languages/wp-user-frontend.pot (2)
7-7: POT timestamp looks correctPOT-Creation-Date updated to 2025-11-17T05:45:00Z. No issues.
208-215: Reference-only updates acknowledgedThese appear to be reference/offset churn with unchanged msgids. Safe to ship.
Also applies to: 529-551, 556-559, 608-612, 674-676, 695-698, 706-724, 731-744, 772-775, 787-790, 838-844, 875-878, 896-898, 914-916, 981-983, 985-987, 990-992, 1006-1012
Lib/WeDevs_Settings_API.php (2)
144-144: LGTM!The addition of
depends_on_valueto the args array follows the same pattern as the existingdepends_onfield and properly handles optional values.
385-409: LGTM!The select field changes mirror the text field implementation correctly, with proper array dependency handling and data attribute sanitization.
| #: includes/Integrations/WPUF_N8N_Integration.php:163 | ||
| #: includes/Integrations/WPUF_N8N_Integration.php:309 | ||
| msgid "N8N" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:185 | ||
| msgid "Authentication Type" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:186 | ||
| msgid "Select the authentication type for the N8N integration." | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:193 wpuf-functions.php:3682 | ||
| msgid "None" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:194 | ||
| msgid "Basic Auth" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:195 | ||
| msgid "Header Auth" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:196 | ||
| msgid "JWT Auth" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:201 | ||
| msgid "Basic Auth Username" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:202 | ||
| msgid "Enter the username for the Basic Auth authentication." | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:212 | ||
| msgid "Basic Auth Password" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:213 | ||
| msgid "Enter the password for the Basic Auth authentication." | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:225 | ||
| msgid "Enter the name for the Header Auth authentication." | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:235 | ||
| msgid "Header Auth Value" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:236 | ||
| msgid "Enter the value for the Header Auth authentication." | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:247 | ||
| msgid "JWT Key Type" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:248 | ||
| msgid "Select the type of JWT key for the JWT Auth authentication." | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:255 | ||
| #: includes/Integrations/WPUF_N8N_Integration.php:263 | ||
| msgid "Passphrase" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:256 | ||
| #: includes/Integrations/WPUF_N8N_Integration.php:277 | ||
| msgid "PEM Key" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:264 | ||
| msgid "Enter the passphrase for the JWT Auth authentication." | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:278 | ||
| msgid "Enter the PEM key for the JWT Auth authentication." | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:330 | ||
| msgid "Enable N8N Integration" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:332 | ||
| msgid "Enable N8N integration" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:337 | ||
| msgid "Webhook URL" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:339 | ||
| msgid "Enter the N8N webhook URL to send form data when posts are submitted" | ||
| msgstr "" | ||
|
|
||
| #: includes/Integrations/WPUF_N8N_Integration.php:340 | ||
| msgid "https://your-n8n-instance.com/webhook/your-webhook-id" | ||
| msgstr "" | ||
|
|
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.
n8n brand, capitalization, and context consistency
- Use “n8n” (lowercase) consistently.
- Unify label/description casing: “Enable n8n integration”.
- Disambiguate “Name” for header auth; prefer “Header Name” or add msgctxt to avoid collision with generic “Name”.
Proposed source changes (then regenerate POT):
- __( 'N8N', 'wp-user-frontend' )
+ __( 'n8n', 'wp-user-frontend' )
- __( 'Enable N8N Integration', 'wp-user-frontend' )
+ __( 'Enable n8n integration', 'wp-user-frontend' )
- __( 'Enable N8N integration', 'wp-user-frontend' )
+ __( 'Enable n8n integration', 'wp-user-frontend' )
- __( 'Webhook URL', 'wp-user-frontend' )
+ __( 'Webhook URL', 'wp-user-frontend' ) // keep label
- __( 'Enter the N8N webhook URL to send form data when posts are submitted', 'wp-user-frontend' )
+ __( 'Enter the n8n webhook URL to send form data when posts are submitted', 'wp-user-frontend' )
- __( 'Name', 'wp-user-frontend' ) // used as Header Auth name
+ _x( 'Header Name', 'n8n header authorization key label', 'wp-user-frontend' )
- __( 'Authentication Type', 'wp-user-frontend' ) // keep
+ _x( 'Authentication Type', 'n8n integration setting', 'wp-user-frontend' )If you prefer to keep “Name”, add msgctxt instead:
_x( 'Name', 'n8n header authorization key label', 'wp-user-frontend' );Also update the sample text:
- "https://your-n8n-instance.com/webhook/your-webhook-id"
+ "https://your-n8n-instance.com/webhook/your-webhook-id"
# (no change to URL itself; only ensure surrounding descriptions use "n8n")I can open a small follow-up PR touching includes/Integrations/WPUF_N8N_Integration.php to apply this.
🤖 Prompt for AI Agents
In languages/wp-user-frontend.pot around lines 7204 to 7306, update branding and
label consistency: change all "N8N" to "n8n" (e.g., msgid "N8N" -> "n8n"),
update labels to use sentence-style casing like "Enable n8n integration" and
"Enable n8n Integration" occurrences, disambiguate the generic "Name" used for
header auth by replacing it with "Header Name" (or use _x( 'Name', 'n8n header
authorization key label', 'wp-user-frontend' ) in the source to add msgctxt),
and update the sample webhook URL to use lowercase "n8n" (e.g.,
"https://your-n8n-instance.com/...") before regenerating the POT.
| if (expected_value === '' || expected_value === null || expected_value === undefined) { | ||
| // Empty expected value means "show when checked" (typical use case) | ||
| value_matches = is_checked; | ||
| } else { | ||
| $this.closest('tr').hide(); | ||
| // Non-empty expected value means "show when checked with this value" | ||
| value_matches = is_checked && (current_value === expected_value || expected_value === 'on'); | ||
| } | ||
| }); | ||
| } else { | ||
| value_matches = current_value === expected_value; | ||
| } | ||
|
|
||
| if (!value_matches) { | ||
| all_dependencies_met = false; | ||
| } | ||
| } | ||
|
|
||
| // Show/hide based on all dependencies | ||
| if (all_dependencies_met) { | ||
| $this.closest('tr').show(); | ||
| } else { | ||
| $depends_on.on('keyup change', function() { | ||
| if ($(this).val() === $this.data('depends-on-value')) { | ||
| $this.closest('tr').show(); |
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.
Radio button value comparison broken.
For radio buttons, .val() on a jQuery collection returns the value attribute of the first element, not the checked one. This prevents radio-based dependencies from working correctly.
The checkbox handling partially addresses the previous review's concerns, but radio buttons still need special treatment. Apply this diff to fix radio button handling in both the initial check and checkAllDependencies():
In the initial dependency check (around line 870):
dependency_fields[field_name] = $depends_on;
var current_value = $depends_on.val();
var is_checkbox = $depends_on.attr('type') === 'checkbox';
+ var is_radio = $depends_on.attr('type') === 'radio';
var is_checked = is_checkbox ? $depends_on.is(':checked') : null;
// For checkboxes, handle empty string as "checked" expectation
var value_matches = false;
if (is_checkbox) {
if (expected_value === '' || expected_value === null || expected_value === undefined) {
// Empty expected value means "show when checked" (typical use case)
value_matches = is_checked;
} else {
// Non-empty expected value means "show when checked with this value"
value_matches = is_checked && (current_value === expected_value || expected_value === 'on');
}
+ } else if (is_radio) {
+ // For radio buttons, get the value of the checked one
+ var radio_name = $depends_on.attr('name');
+ var checked_value = $('input[name="'+ radio_name +'"]:checked').val() || '';
+ value_matches = checked_value === expected_value;
} else {
value_matches = current_value === expected_value;
}In checkAllDependencies (around line 940):
var current_value = $depends_on.length > 0 ? $depends_on.val() : 'NOT_FOUND';
var is_checkbox = $depends_on.length > 0 && $depends_on.attr('type') === 'checkbox';
+ var is_radio = $depends_on.length > 0 && $depends_on.attr('type') === 'radio';
var is_checked = is_checkbox ? $depends_on.is(':checked') : null;
// For checkboxes, handle empty string as "checked" expectation
var value_matches = false;
if ($depends_on.length === 0) {
value_matches = false;
} else if (is_checkbox) {
if (expected_value === '' || expected_value === null || expected_value === undefined) {
// Empty expected value means "show when checked" (typical use case)
value_matches = is_checked;
} else {
// Non-empty expected value means "show when checked with this value"
value_matches = is_checked && (current_value === expected_value || expected_value === 'on');
}
+ } else if (is_radio) {
+ // For radio buttons, get the value of the checked one
+ var radio_name = $depends_on.attr('name');
+ var checked_value = $('input[name="'+ radio_name +'"]:checked').val() || '';
+ value_matches = checked_value === expected_value;
} else {
value_matches = current_value === expected_value;
}Also applies to: 940-964
🤖 Prompt for AI Agents
In Lib/WeDevs_Settings_API.php around lines 870-892 and 940-964, radio-button
dependency checks use $depends_on.val() which returns the first element's value
instead of the checked radio's value; update both places to detect radio groups
(e.g. $depends_on.is(':radio') or $depends_on.filter(':radio').length) and when
radio inputs are present retrieve the value of the checked radio via
$depends_on.filter(':checked').val() (and treat absence as unchecked), then
perform comparisons against expected_value (mirroring checkbox logic: empty
expected_value means show when checked, otherwise compare checked value or
'on'), ensuring you branch for radio vs checkbox vs other inputs in both the
initial check and in checkAllDependencies().
…tion_for_post_form
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: 1
♻️ Duplicate comments (1)
wpuf-functions.php (1)
4043-4087: Critical issue from previous review remains unresolved.The past review identified that this password preview field will corrupt the stored secret on form submission. The input on line 4079 binds the real option name to the masked preview value, so submitting the form will save the masked string (e.g.,
ab****yz) to the database instead of preserving the actual secret.Please implement the fix provided in the previous review comment to avoid data corruption.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
wpuf-functions.php(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
wpuf-functions.php (1)
Lib/WeDevs_Settings_API.php (3)
WeDevs_Settings_API(13-984)get_option(636-645)get_field_description(162-170)
| /** | ||
| * Password preview field callback | ||
| * | ||
| * @param array $args Field arguments | ||
| * @since WPUF_PRO_SINCE | ||
| */ | ||
| function wpuf_settings_password_preview( $args ) { | ||
| wpuf_require_once( WPUF_ROOT . '/Lib/WeDevs_Settings_API.php' ); | ||
|
|
||
| $settings = new WeDevs_Settings_API(); | ||
| $value = $settings->get_option( $args['id'], $args['section'], $args['std'] ); | ||
| $disabled = ! empty( $args['is_pro_preview'] ) && $args['is_pro_preview'] ? 'disabled' : ''; | ||
| $size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular'; | ||
|
|
||
| // Create masked preview of the password | ||
| $preview_value = ''; | ||
| if ( ! empty( $value ) ) { | ||
| $length = strlen( $value ); | ||
| if ( $length >= 4 ) { | ||
| $preview_value = substr( $value, 0, 2 ) . str_repeat( '*', $length - 4 ) . substr( $value, -2 ); | ||
| } else { | ||
| $preview_value = str_repeat( '*', $length ); | ||
| } | ||
| } | ||
|
|
||
| $depends_on = ! empty( $args['depends_on'] ) ? $args['depends_on'] : ''; | ||
| $depends_on_value = ! empty( $args['depends_on_value'] ) ? $args['depends_on_value'] : ''; | ||
|
|
||
| // Handle array dependencies | ||
| if (is_array($depends_on)) { | ||
| $depends_on_json = esc_attr( json_encode($depends_on) ); | ||
| $depends_on_value = ''; // Not used for array format | ||
| } else { | ||
| $depends_on_json = esc_attr( $depends_on ); | ||
| } | ||
|
|
||
| $html = sprintf( '<input type="text" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s" %5$s data-depends-on=\'%6$s\' data-depends-on-value="%7$s"/>', $size, $args['section'], $args['id'], $preview_value, $disabled, $depends_on_json, esc_attr( $depends_on_value ) ); | ||
| $html .= $settings->get_field_description( $args ); | ||
|
|
||
| if ( ! empty( $args['is_pro_preview'] ) && $args['is_pro_preview'] ) { | ||
| $html .= wpuf_get_pro_preview_html(); | ||
| } | ||
|
|
||
| echo wp_kses( $html, array( 'input' => array( 'type' => array(), 'class' => array(), 'id' => array(), 'name' => array(), 'value' => array(), 'readonly' => array(), 'style' => array(), 'disabled' => array(), 'data-depends-on' => array(), 'data-depends-on-value' => array() ), 'p' => array( 'class' => array() ), 'div' => array( 'class' => array() ), 'a' => array( 'href' => array(), 'target' => array(), 'class' => array() ), 'span' => array( 'class' => array() ), 'svg' => array( 'width' => array(), 'height' => array(), 'viewBox' => array(), 'fill' => array(), 'xmlns' => array() ), 'path' => array( 'd' => array(), 'fill' => array() ) ) ); | ||
| } |
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 duplicate function declaration.
According to the AI summary, this function wpuf_settings_password_preview is declared twice in this file. Duplicate function declarations can cause maintenance issues and potential conflicts.
Please remove the duplicate declaration and keep only one instance.
Based on learnings.
🤖 Prompt for AI Agents
In wpuf-functions.php around lines 4043-4087 there's a duplicate declaration of
function wpuf_settings_password_preview; remove the other duplicate declaration
(whichever is outside these lines) so only a single definition remains, and
ensure any helper logic or differences from the duplicate are merged into the
kept version before deleting; finally run a quick search for
wpuf_settings_password_preview to confirm only one declaration exists and run
lint/tests to verify no breakage.
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: 2
♻️ Duplicate comments (1)
languages/wp-user-frontend.pot (1)
7216-7316: n8n branding and header-auth label context still need adjustmentThe N8N integration strings here still show the earlier issues:
- Brand capitalization: official branding is
n8n, but msgids use"N8N"("N8N","Enable N8N Integration","Enable N8N integration","Enter the N8N webhook URL..."). Consider switching to"n8n"consistently.- Header Auth label: the generic
"Name"label (referenced at line 861 and here viaincludes/Integrations/WPUF_N8N_Integration.php:224) is reused in other contexts, which may confuse translators and users. Renaming to"Header Name"or adding a context (msgctxt) in the PHP source for the n8n header key would disambiguate it.- If you keep
"Authentication Type"generic, consider adding a msgctxt in PHP for the n8n settings usage.These changes should be done in the PHP source (e.g.,
includes/Integrations/WPUF_N8N_Integration.php), then the POT regenerated so the msgids match. This mirrors the earlier review feedback and remains unresolved.
🧹 Nitpick comments (1)
assets/css/admin.css (1)
732-740: CSS rule additions look good, but consider reducing property duplication.The new selectors
.form-table th span.pro-iconand.form-table label span.pro-iconredefinedisplay: inline-flex,align-items: center, andvertical-align: middle—all already declared in the parentspan.pro-iconrule (line 720–731). Only the new adjustments (line-height: 0,position: relative,top: -1px) are needed here.Apply this diff to eliminate duplication:
.form-table th span.pro-icon, .form-table label span.pro-icon { - display: inline-flex; - align-items: center; - vertical-align: middle; line-height: 0; position: relative; top: -1px; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
admin/form-builder/assets/js/form-builder.js(1 hunks)assets/css/admin.css(1 hunks)assets/css/admin/form-builder.css(5 hunks)assets/css/ai-form-builder.css(5 hunks)languages/wp-user-frontend.pot(35 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- assets/css/admin/form-builder.css
🔇 Additional comments (7)
admin/form-builder/assets/js/form-builder.js (1)
1292-1299: LGTM! Clean integration for N8N webhook URL dependency.The new
n8n_webhook_urlfield configuration correctly follows the established pattern for conditional field visibility. The dependency logic appropriately shows the webhook URL input only when N8N integration is enabled, and it will work seamlessly with the existingFormDependencyHandlerclass.languages/wp-user-frontend.pot (4)
7-7: POT header regeneration looks correct
POT-Creation-Datewas updated as expected for a regenerated catalog; no issues here.
208-215: Reference/line-number shifts onlyThe
#: ...source references updated here (and similarly throughout the file) only adjust file/line mappings without changing anymsgidtext, so there’s no functional or localization impact.
1866-2025: AI error and status messages are well-structuredThe new AI-related strings (
includes/AI/Config.phpandincludes/AI/RestController.php) are clear, consistently phrased, and use printf-style placeholders (%s,%d) correctly, which should be safe for translators and runtime formatting.
457-462: Issue #1257: required strings now present in POTFor the translation coverage issue:
"Preview"(lines 457–462) is present and referenced fromtemplates/dashboard/posts.php:167."View"(lines 8642–8643) is present and referenced fromtemplates/dashboard/posts.php:164(Status column options)."Unlimited"(lines 7469–7472) is present and referenced fromtemplates/dashboard/subscription.php:188and related paths.This satisfies the POT-side requirement from issue #1257; these UI strings are now available for translators.
Also applies to: 7469-7472, 8642-8643
assets/css/ai-form-builder.css (2)
1097-1097: Well done: Vendor prefix removal for backdrop-filter.The removal of
-webkit-backdrop-filterfrom transition-property declarations (e.g., lines 1097, 1884, 2573) and the use of standardbackdrop-filterthroughout (lines 3025, 3045) aligns with modern browser support. This is a solid cleanup that reduces CSS bloat without compromising compatibility.Also applies to: 1884-1884, 2573-2573, 3025-3025, 3045-3045
5331-5333: Utilities are consumed; confirm focus-variant intent.Found direct usages:
- assets/js/components/FormsList.vue:601 — wpuf-bg-emerald-50
- assets/js/components/FormSuccessStage.vue:333 — wpuf-bg-emerald-50
- assets/js/components/subscriptions/SectionInputField.vue:310 — wpuf-bg-emerald-50/50, wpuf-backdrop-blur-sm
Compiled/minified assets include the focus variant definition (focus:wpuf-bg-emerald-700) but no template references were detected — confirm if that focus utility is intentionally retained (used dynamically) or can be removed to avoid extra CSS weight.
| /* span.pro-icon.icon-white svg path { | ||
| fill: #fff; | ||
| fill: #fff; | ||
| } */ | ||
| /* label span.pro-icon svg path { | ||
| fill: #10b981; | ||
| fill: #10b981; | ||
| } */ |
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 | 🟠 Major
Remove or explain commented-out code.
Lines 741–746 contain two commented-out CSS rules for icon fill styling. Either remove this dead code or add an inline comment explaining why it's disabled (e.g., TODO, deprecated feature, etc.).
Apply this diff to clean up:
-/* span.pro-icon.icon-white svg path {
- fill: #fff;
-} */
-/* label span.pro-icon svg path {
- fill: #10b981;
-} */📝 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.
| /* span.pro-icon.icon-white svg path { | |
| fill: #fff; | |
| fill: #fff; | |
| } */ | |
| /* label span.pro-icon svg path { | |
| fill: #10b981; | |
| fill: #10b981; | |
| } */ |
🤖 Prompt for AI Agents
In assets/css/admin.css around lines 741 to 746, there are two commented-out CSS
rules for icon fill styling; either remove these dead lines or replace them with
a brief inline comment explaining why they're disabled (for example: "TODO:
deprecated - kept for reference", "disabled due to design change X on DATE", or
link to an issue/PR). Update the file by deleting the commented rules if they
are no longer needed, or leave a single-line comment above them that states the
reason and references any relevant ticket, then save.
| #: includes/Free/Form_Element.php:188 includes/Free/Free_Loader.php:1585 | ||
| msgid "Multistep Progressbar Type" | ||
| msgstr "" | ||
|
|
||
| #: includes/Free/Form_Element.php:198 includes/Free/Free_Loader.php:1589 | ||
| #: includes/Free/Form_Element.php:198 includes/Free/Free_Loader.php:1587 | ||
| msgid "Choose how you want the progressbar" | ||
| msgstr "" | ||
|
|
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.
Polish a few new UI strings for grammar and clarity
These new labels/descriptions are understandable but could be tightened for correctness:
- Line 5475:
"Multistep Progressbar Type"→ consider"Multi-step Progress Bar Type". - Line 5480:
"Choose how you want the progressbar"→ consider"Choose how you want the progress bar to appear". - Line 9399:
"After successfully submit, where the page will redirect to"→ consider"After successful submission, where should the page redirect to?"or"After successful submission, where the page will redirect to". - Line 9489:
"Choose who can submit post"→ consider"Choose who can submit posts".
These are minor UX copy fixes but will make the settings panel read more professionally.
Also applies to: 9399-9401, 9488-9491
🤖 Prompt for AI Agents
languages/wp-user-frontend.pot lines 5475-5482, 9399-9401, 9488-9491: replace
the indicated message strings to improve grammar and clarity — change "Multistep
Progressbar Type" to "Multi-step Progress Bar Type", change "Choose how you want
the progressbar" to "Choose how you want the progress bar to appear", change
"After successfully submit, where the page will redirect to" to "After
successful submission, where should the page redirect to?" (or the alternative
"After successful submission, where the page will redirect to"), and change
"Choose who can submit post" to "Choose who can submit posts"; update the msgstr
entries accordingly in each location.


closes #1027, fix #1257
Summary
This PR introduces N8N workflow automation integration for post forms, allowing form submissions to automatically trigger N8N webhooks. The integration supports multiple authentication methods (Basic Auth, Header Auth, and JWT) and sends comprehensive post data including custom fields to configured N8N webhooks. Additionally, this PR includes significant enhancements including an AI Form Builder feature, subscription pack design improvements, and various bug fixes and UI refinements.
Testing Recommendations
N8N Integration:
Summary by CodeRabbit
New Features
New/Improved Features
Style
Chores
✏️ Tip: You can customize this high-level summary in your review settings.