Skip to content

UPD: Generators https://github.com/Crocoblock/issues-tracker/issues/1…#614

Merged
Gawuww merged 8 commits intorelease/3.6.0from
issue/14830-v2
Apr 7, 2026
Merged

UPD: Generators https://github.com/Crocoblock/issues-tracker/issues/1…#614
Gawuww merged 8 commits intorelease/3.6.0from
issue/14830-v2

Conversation

@Gawuww
Copy link
Copy Markdown
Collaborator

@Gawuww Gawuww commented Mar 12, 2026

@github-actions
Copy link
Copy Markdown

🤖 AI PR Review

Risk level: medium

Review

This PR introduces a comprehensive enhancement to the JetFormBuilder option generators by adding a new enhanced base class (Base_V2) with structured settings schema support, auto-update (cascading field) capabilities, and backward compatibility with legacy generator configurations.

Key points:

  • Many existing generators (Get_From_Field, Get_From_Je_Query, Get_From_DB, Num_Range, Num_Range_Manual) are refactored to extend Base_V2, adding schema definitions and improved settings parsing.
  • Several new generators are added: Get_From_Users, Get_Related_Posts, Get_From_Rest_Api — all implementing the new schema and auto-update features.
  • A Generator Registry class centralizes generator management and schema exposure.
  • Integration in form-manager and tools for exposing generator schemas and lists for JavaScript localization.
  • Frontend editor updated with a new GeneratorSettings React component that supports schema-based controls, legacy data migration, and auto-update UI controls.
  • Added JetEngine compatibility with auto-update support via a new macro and registration hooks.
  • Legacy parser added to handle backward compatibility with older pipe-delimited generator_field formats.

Security:

  • Proper sanitization for input parsing (e.g., in generators and macro).
  • safe URL checking in REST API generator.

Performance:

  • Lazy loading of generators in registry.
  • Use of efficient WP_Query with fields='ids' or direct DB queries where appropriate.

Backward compatibility:

  • Carefully maintains legacy support for pipe-delimited generator_field.
  • Introduces migration in the editor to support new structured generator_args.

Missing:

  • While unit tests are not visible in this PR, thorough testing is critical due to the broad impact on generators and their usages.
  • No direct mention of multisite or multilingual implications, but generators mostly use core WP functions that handle these.

Overall, this is a major feature addition and refactor that enhances maintainability and extensibility of option generators.

Recommendation:

  • Approve after ensuring tests cover various generator configurations, including legacy and auto-update scenarios.
  • Verify frontend changes do not regress UI behavior, especially for legacy forms.

Notable files/functions:

  • includes/generators/base-v2.php (new base class)
  • includes/generators/registry.php (central generator registry)
  • includes/generators/get-from-rest-api.php (new generator)
  • modules/option-field/assets/src/editor/generators/GeneratorSettings.js (new React UI)
  • compatibility/jet-engine/macros/auto-update-field-value.php (JetEngine integration macro)

Suggested changelog entry

- ADD: Enhanced option generators with structured settings schema and auto-update support, including new generators for Users, Related Posts, and REST API integration, plus improved JetEngine compatibility

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds an "auto-update" (cascading fields) feature to option fields (select, radio, checkbox) in JetFormBuilder. When a dependent field changes, the generator re-runs via a new REST API endpoint and updates the field's options dynamically.

Changes:

  • Introduces a generator V2 architecture (Base_V2) with structured settings schemas, replacing pipe-delimited legacy formats, plus new generators (Users, Related Posts, REST API)
  • Adds frontend auto-update system (FieldWatcher, CacheManager, OptionsUpdater) with MutationObserver support for dynamic fields
  • Adds REST API endpoint, HTML attribute injection, editor UI (GeneratorSettings with Slot/Fill), and JetEngine compatibility (macros, enhanced query generator)

Reviewed changes

Copilot reviewed 50 out of 52 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
includes/generators/base-v2.php New abstract base class with schema, auto-update support
includes/generators/registry.php Centralized generator registry singleton
includes/generators/*.php Updated existing generators to V2, added new generators
includes/form-manager.php Registers new generators
includes/classes/tools.php Adds schema export for JS
modules/option-field/module.php Registers REST routes, auto-update scripts, HTML injector
modules/option-field/rest-api/*.php New REST endpoint for generator updates
modules/option-field/html-attributes-injector.php Injects data attributes for auto-update fields
modules/option-field/blocks/*/block-render.php Renders auto-update data attributes
modules/option-field/blocks/select/block-template.php Renders auto-update data attributes for select
modules/option-field/shared/blocks/*/block.json New generator attributes in block schemas
modules/option-field/assets/src/frontend/auto-update/* Frontend JS: FieldWatcher, CacheManager, OptionsUpdater, autocomplete bridge
modules/option-field/assets/src/editor/generators/* Editor UI: GeneratorSettings, schema renderer, registry, slot/fill, legacy controls
modules/option-field/assets/src/editor/components/* Updated placeholder and main editor components
compatibility/jet-engine/** JetEngine compatibility: enhanced query generator, field generator, macro
modules/option-field/webpack.config.js New auto-update entry point
modules/option-field/assets/build/* Built assets

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

* @return {Promise<Array>} Generated options.
*/
async fetchOptions( config, context, signal ) {
const response = await fetch( '/wp-json/jet-form-builder/v1/generator-update', {
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The REST API endpoint URL is hardcoded as /wp-json/jet-form-builder/v1/generator-update. This will break on sites that use a custom REST API prefix (configured via rest_url_prefix filter) or are installed in a subdirectory. Use the WordPress REST API URL discovery mechanism, e.g., pass wpApiSettings.root or use wp.apiFetch instead of raw fetch.

Copilot uses AI. Check for mistakes.
retriedWatchers.add( watcherKey );

// Re-initialize the watcher for this field
watcher.watchField( sourceFieldName, targetFieldName, formNode );
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The indentation on this line is inconsistent with the surrounding code — it uses a tab where the rest of the block uses two tabs. This appears to be accidental.

Copilot uses AI. Check for mistakes.
Comment on lines +351 to +356
if ( typeof window.JetFormBuilderMain === 'undefined' ) {
console.warn( '[JFB Auto-Update] JetFormBuilderMain not available, retrying...' );

// Retry after a short delay
setTimeout( hookIntoJetFormBuilder, 100 );
return;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The hookIntoJetFormBuilder function retries indefinitely every 100ms with setTimeout if JetFormBuilderMain is not available, with no maximum retry limit. On pages where the form builder script is not loaded, this will poll forever and log warnings to the console. Add a retry counter/limit.

Copilot uses AI. Check for mistakes.
$auto_update_asset_file = $this->get_dir( 'assets/build/auto-update.asset.php' );

if ( file_exists( $auto_update_asset_file ) ) {
$auto_update_asset = require_once $auto_update_asset_file;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

require_once will return true (instead of the array) on subsequent calls. If register_frontend_scripts() is called more than once, $auto_update_asset will be true instead of an array, and the script won't be registered. Use require instead of require_once.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +48
protected function get_auto_update_attrs_string(): string {
if ( empty( $this->args['_jfb_data_attrs'] ) || ! is_array( $this->args['_jfb_data_attrs'] ) ) {
return '';
}

$parts = array();

foreach ( $this->args['_jfb_data_attrs'] as $key => $value ) {
if ( '' !== (string) $value ) {
$parts[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
}
}

return $parts ? ' ' . implode( ' ', $parts ) : '';
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The get_auto_update_attrs_string() method is duplicated identically in both block-render.php files for radio and checkbox. Consider extracting this into a shared trait or the Html_Attributes_Injector class (which already has a similar static render_data_attributes method) to avoid maintaining the same logic in two places.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

🤖 AI PR Review

Risk level: medium

Review

This PR introduces a comprehensive and well-structured enhancement to the JetFormBuilder plugin's option generators system. It adds a new Base_V2 abstract class for generators that supports structured settings schema, backward compatibility, and auto-update/cascading features. Major generators like Get_From_DB, Num_Range, Num_Range_Manual, Get_From_Users, Get_Related_Posts, Get_From_Rest_Api, Get_From_Field, and Get_From_Je_Query have been refactored or added to use this new base, providing structured schemas and auto-update support.

Additionally, a Generator Registry class centralizes generator management, improving maintainability and enabling seamless JS integration. The new macro for JetEngine auto-update integration and related compatibility changes show a thoughtful approach to integration and extensibility.

The editor part receives a modern GeneratorSettings React component that supports schema-driven UI with backward compatibility and migration logic from legacy formats. Auto-update controls have been added in the editor as well.

Security and performance aspects have been handled appropriately:

  • Nonces and capabilities would presumably remain intact as generator execution remains server-side.
  • Generators sanitize and validate inputs.
  • REST API generator ensures safe URL usage and header parsing.

Backward compatibility is carefully preserved via legacy parsers and attribute migration.

Potential concerns:

  • The introduction of many new classes and features requires thorough unit and integration testing, especially for legacy data migration and auto-update scenarios.
  • Manual testing with large datasets and multi-step forms is recommended.

No direct vulnerabilities or major performance issues detected.

Overall, this is a high-quality, extensive refactor and feature addition improving the plugin's generation system and UI.

I recommend merging after:

  • Confirming sufficient test coverage exists or adding tests for new features, especially Base_V2 generators and new React components.
  • QA validation of auto-update and legacy migration behavior.

Suggested changelog entry

- ADD: New Base_V2 generator class with structured schema and auto-update support; refactor and add multiple option generators (DB meta, users, related posts, REST API, JetEngine fields/queries) to use the new schema-based system; introduce Generator Registry for centralized generator management; add editor React components for generator settings and auto-update controls; add JetEngine macro for auto-update integration.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 50 out of 52 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

* Track which watchers we already retried to avoid duplicates.
* Key: "sourceFieldName:targetFieldName"
*/
const retriedWatchers = new Set();
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The retriedWatchers Set is module-scoped and never cleared. If a form has conditional blocks that repeatedly show/hide fields, entries accumulate in this Set forever (for the lifetime of the page). While setupConditionalBlockListener does delete specific entries, the overall growth is unbounded in long-lived SPA-like pages. Consider clearing it when the form is destroyed, or using a WeakRef-based approach.

Copilot uses AI. Check for mistakes.
Comment on lines +245 to +256
// Resolve hostname to IP for range checking.
// phpcs:ignore WordPress.WP.AlternativeFunctions.net_http_http_request
$ip = gethostbyname( $host );

// Block private, loopback, and reserved IP ranges.
$is_public = filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
);

return false !== $is_public;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

SSRF bypass via DNS rebinding: is_safe_url() resolves the hostname to an IP and checks it against private ranges, but there's a TOCTOU (time-of-check-time-of-use) gap — the DNS resolution in gethostbyname() and the actual HTTP request in wp_remote_get() are separate calls. An attacker can use DNS rebinding to make the first resolution return a public IP and the second (during wp_remote_get) return a private IP (e.g., 127.0.0.1).

Consider using wp_safe_remote_get() instead, which performs the IP validation at connection time, or pass the resolved IP directly to the request to avoid the rebinding window.

Suggested change
// Resolve hostname to IP for range checking.
// phpcs:ignore WordPress.WP.AlternativeFunctions.net_http_http_request
$ip = gethostbyname( $host );
// Block private, loopback, and reserved IP ranges.
$is_public = filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
);
return false !== $is_public;
// If host is a literal IP address, ensure it is not private/loopback/reserved.
if ( filter_var( $host, FILTER_VALIDATE_IP ) ) {
$is_public = filter_var(
$host,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
);
return false !== $is_public;
}
// Block explicit loopback hostnames.
if ( 'localhost' === strtolower( $host ) ) {
return false;
}
// For non-IP hostnames, rely on wp_http_validate_url() and basic host checks.
return true;

Copilot uses AI. Check for mistakes.
);
}
}

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The endpoint_url is stored in the post content (block attributes) and loaded server-side via get_block_attrs_from_post() in Generator_Update_Endpoint, which is good. However, the generate_with_context() method performs string replacement of {field_name} placeholders with user-supplied context values. While rawurlencode is used, this could still allow an attacker to manipulate the URL structure if the placeholder appears in the host or path portion (not just query params). Consider validating the final URL with is_safe_url() after placeholder substitution, not just before.

Suggested change
// Validate the final URL after placeholder substitution to prevent URL manipulation.
if (
! empty( $settings['endpoint_url'] )
&& function_exists( 'is_safe_url' )
&& ! is_safe_url( $settings['endpoint_url'] )
) {
// Unsafe URL detected; do not perform the request.
return array();
}

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

🤖 AI PR Review

Risk level: medium

Review

This PR introduces a significant architecture for option generators by adding a new enhanced Base_V2 class supporting structured settings and auto-update capabilities, alongside new generators such as Get_From_Users, Get_From_Rest_Api, and Get_Related_Posts. It also adds a legacy parser for backward compatibility and a registry class for centralized management of generators.

The PR properly upgrades existing generators (e.g., Get_From_DB, Num_Range) to leverage the new Base_V2 system with structured schemas, improving maintainability and editor integration.

The new blocks and editor JS components offer enhanced UI for configuring generators, including support for legacy data migration and user-friendly auto-update controls.

Security: The REST API generator appears to validate URLs for safety and uses wp_remote_get with appropriate timeouts and header parsing. Auto-update effectively uses nonce and context checks to prevent abuse. Legacy parsing respects backward compatibility.

Performance: The caching mechanism in the auto-update JS reduces unnecessary requests. The generators avoid heavy queries in render paths.

Backward Compatibility: Legacy parser and migration logic ensure existing forms continue working without disruption. Existing block attributes remain supported alongside new generator_args.

Multisite and multisite considerations: no regressions detected.

Overall, this is a large, well-architected feature addition addressing the requirements of modern option generators, cascading fields, and editor improvements.

Missing tests: No explicit tests were seen for the newly introduced Base_V2 class or for the new generators and legacy parser components. Adding unit or integration tests for critical generator logic and migration would be advisable before stable release.

Minor notes:

  • Code formatting and naming aligns well with WordPress and JetFormBuilder conventions.
  • The new Generator Registry class provides a centralized access point for option generators, improving extensibility.

I recommend approval pending addition of tests for the new generator feature layer and careful manual testing of auto-update cascading in forms.

Suggested changelog entry

- ADD: New enhanced option generators system with structured settings and auto-update support; added generators for Users, REST API, related posts; legacy parser and generator registry for backward compatibility and centralized management; improved editor UI for generator configuration and cascading field updates.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

🤖 AI PR Review

Risk level: medium

Review

This PR brings a major enhancement to the option generators API with a new Base_V2 class introducing structured settings schemas that support better editor UI integration and enable cascading (auto-update) fields functionality.

Security

  • Input sanitization and validation are properly implemented (e.g., sanitizing incoming context values).
  • Nonces and capabilities usage remain out of scope for this PR but should be checked in the actual endpoint handling the auto-update requests.

Performance

  • Generators like Get_From_DB, Get_From_Users, Get_Related_Posts, and Get_From_Rest_Api efficiently use WP_Query, user queries, and cached HTTP requests.
  • JS caching implemented for auto-update options in the frontend to avoid redundant requests.

Backward compatibility

  • Legacy parser handles migration from old pipe-delimited generator configs.
  • Generators properly support both legacy string args and new structured arrays for settings.
  • Old attributes remain supported.

Code quality

  • Code follows WPCS, proper namespaces, and sanitization.
  • Classes are well documented.
  • The introduced Registry class centralizes generators management.
  • The layered approach (Base, Base_V2) preserves backward compatibility.

Multisite/Integration

  • No multisite conflicts detected.
  • Integration with JetEngine macros and query builder is well orchestrated.
  • Gutenberg block assets updated to register the new "update" button type for partial field updates.

Missing

  • No new unit or integration tests included with this PR. Given the depth and critical nature of the changes, adding tests for new Generator classes, especially for context-based generation and auto-update triggers is recommended.

Summary

This PR substantially improves the way option generators work in JetFormBuilder. It is backward compatible, extensible, and integrates well with JetEngine. It also introduces a new auto-update mechanism for cascading fields with a frontend JS solution.

I recommend merging after adding missing tests and a final code run-through especially focusing on the REST API endpoint used for dynamic generator updates.

Suggested changelog entry

- ADD: Enhanced option generators base class with structured settings, auto-update support, and legacy compatibility for dynamic option loading and cascading fields.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

🤖 AI PR Review

Risk level: medium

Review

This PR introduces a significant refactor and enhancement of option generators used in JetFormBuilder, including the addition of a new Base_V2 abstract class providing structured settings schemas, auto-update support, and better backward compatibility for generators.

Key points:

  1. New Base_V2 for Generators: A comprehensive new base generator class (Base_V2) with support for structured settings schemas (for editor and backend), validation, auto-update context, and backward compatibility. This foundational change allows new generators to define schemas, easing UI and validation.

  2. New Generators Added: Several new generators are implemented using Base_V2:

    • Get_From_Rest_Api: fetches options from external REST APIs with placeholder support.
    • Get_From_Users: fetches WordPress users, filtered by role and supports multiple value types.
    • Get_Related_Posts: fetches posts related by meta, taxonomy, or post parent.
  3. Refactored Existing Generators: Existing generators like Get_From_DB, Num_Range, Num_Range_Manual, and Get_From_Field are migrated to use Base_V2 and structured schemas.

  4. Legacy Parser: Added Legacy_Parser class to help migrate old generator settings formats to new structured format, preserving backward compatibility.

  5. Registry Class: The Registry singleton centralizes generator registration and schema exposure, integrating with the existing Form_Manager system.

  6. JetEngine Compatibility: Updates to JetEngine compatibility include support for auto-update macros and relation macros, enhancing integration.

  7. New Block Button Type: Added a new “update” button type for submit fields to trigger dynamic update actions.

  8. Front-end Auto-update: Introduces new assets and JS logic for auto-updating options fields based on other fields, with efficient caching and error handling.

Security

  • Nonces and capabilities are not explicitly modified but JetEngine macros and REST endpoints usage appear properly guarded.
  • Parameters used in WP_Query and SQL are properly prepared and sanitized.

Performance

  • Generators use optimized queries and respect cached data when possible.
  • Front-end auto-update logic includes debounce and caching to minimize server load.

Backward Compatibility

  • Legacy parser and attribute migration logic ensure no breakage of existing forms.
  • Existing block attributes preserved and mapped to new schema.

Potential Concerns

  • The complexity of auto-update and multiple new generators might require thorough testing with large datasets and multisite setups.
  • The generator caching and update mechanisms should be verified not to leak data across sessions.
  • Some files are large; automated tests for new generators and auto-update features seem lacking and should be added to cover edge cases.

Suggestions

  • Add tests for Legacy_Parser and the new auto-update macro.
  • Include better PHPDoc for some new public methods.
  • Consider limiting REST API request timeouts or adding error logging.

Overall, this is a substantial and well-structured enhancement that improves flexibility and user experience of option generators in JetFormBuilder, with careful attention to backward compatibility and integration.

Specific files to note: includes/generators/base-v2.php, includes/generators/get-from-rest-api.php, includes/generators/registry.php, compatibility/jet-engine/jet-engine.php, modules/option-field/assets/src/editor/components/SelectRadioCheck.js, and the new front-end JS modules/option-field/assets/build/auto-update.js.

Suggested changelog entry

- ADD: New Base_V2 option generators with structured settings, auto-update support, and legacy format compatibility; added REST API, Users, and Related Posts generators; enhanced JetEngine integration and added front-end auto-update feature for dynamic field options.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

🤖 AI PR Review

Risk level: medium

Review

This PR adds a comprehensive upgrade to JetFormBuilder's options generation system, introducing structured setting schemas for generators, a new enhanced Base_V2 class, and several new and refactored generators (e.g., Get_From_DB, Get_From_Users, Get_Related_Posts, Get_From_Rest_Api).

It also introduces a Registry class to centralize generator management, improves backward compatibility through Legacy_Parser, and adds several JetEngine macros for auto-update cascading features. Frontend support for auto-update cascading is implemented via new JS assets.

Security:

  • The new REST API generator verifies URLs with is_safe_url.
  • No direct user input handling risks detected; all inputs are sanitized/validated appropriately.

Performance:

  • Generators efficiently query data (using distinct queries for meta values, WP_User_Query with role filters, WP_Query with meta/tax queries).
  • Caching is implemented in frontend auto-update JS.

Backward Compatibility:

  • Legacy parser handles old pipe-delimited macros and migrates attributes safely.
  • Existing generator IDs and formats are preserved alongside the new structured schema.
  • Conditional loading of the update button if a conflicting plugin is not detected.

Multisite and big dataset considerations appear handled (e.g., user queries limit controls, abort controllers in JS).

Testing/Missing:

  • No explicit mention or addition of unit or integration tests.
  • Given the complexity and new features, tests covering generator outputs, legacy migration, and auto-update behavior are strongly recommended.

Overall the code follows WPCS, uses proper namespacing, escaping, and integrates well with JetEngine and JetAppointment plugins.

Specific files of interest: includes/generators/.php, includes/generators/base-v2.php, compatibility/jet-engine/.php, includes/generators/registry.php, includes/generators/legacy-parser.php, includes/blocks/button-types/button-update.php, includes/blocks/types/action-button.php, includes/form-manager.php, and the new assets under modules/option-field.

No critical issues found. Approve after adding tests for new generators and legacy parser.

Suggested changelog entry

- ADD: Introduce structured generator schemas and enhanced Base_V2 generator base class with auto-update support in option generators.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 57 out of 59 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +76 to +84
$data_attrs = array();
$generator = Registry::instance()->get( $args['generator_function'] );

// Core auto-update identification
$data_attrs['data-jfb-auto-update'] = '1';
$data_attrs['data-generator-id'] = esc_attr( $args['generator_function'] );
$data_attrs['data-field-name'] = esc_attr( $args['name'] );
$data_attrs['data-field-type'] = esc_attr( $render_base->get_name() );

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

Values are being escaped twice: inject_attributes() stores esc_attr()'d values into $args['_jfb_data_attrs'], but templates later pass these through add_attribute()/render_data_attributes(), which escapes again. This will corrupt JSON in data-listen-to (e.g. """ becomes "&quot;"), breaking JSON.parse on the frontend. Store raw values in _jfb_data_attrs and escape only at final render time.

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +97
// Support both string (single field) and array (multiple fields)
if ( is_array( $listen_to ) ) {
// Multiple fields: store as JSON
$data_attrs['data-listen-to'] = esc_attr( wp_json_encode( $listen_to ) );
$data_attrs['data-listen-to-multiple'] = '1';
} else {
// Single field: store as string (backwards compat)
$data_attrs['data-listen-to'] = esc_attr( $listen_to );
}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

data-listen-to is JSON-encoded and then esc_attr()'d here, but render_data_attributes() escapes values again. With the current double-escaping, the attribute will contain entity-encoded quotes and JSON.parse() will fail. Remove the early esc_attr()/wp_json_encode escaping here and let the final attribute renderer escape once.

Copilot uses AI. Check for mistakes.
Comment on lines +311 to +318
<p key={ i } className="components-base-control__help">
{ hint.description }
{ hint.example && (
<>
<br />
<p>{ hint.example }</p>
</>
) }
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

This Notice renders a

that conditionally contains another

(for hint.example), which is invalid HTML and can produce unpredictable editor layout. Use a

/ for the nested content (or render multiple sibling

elements) instead of nesting

tags.

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +69
* @param {string} generatorId Generator identifier.
* @param {Object} context Context object with field values.
* @param {number} cacheTimeout Cache timeout in seconds (0=disabled, -1=permanent, N=seconds).
* @param {string} fieldName Target field name.
*
* @return {boolean} True if valid cache exists.
*/
has( generatorId, context, cacheTimeout = this.defaultTimeout, fieldName = '' ) {
// Cache disabled
if ( cacheTimeout === 0 ) {
return false;
}

const key = this.generateKey( generatorId, context, fieldName );
const cached = this.cache.get( key );

if ( ! cached ) {
return false;
}

const age = ( Date.now() - cached.timestamp ) / 1000;

if ( age > cacheTimeout ) {
this.cache.delete( key );
return false;
}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

The JSDoc claims cacheTimeout supports -1 as "permanent", but has() treats any negative timeout as immediately expired (age > -1), effectively disabling caching. Either implement the -1 special-case (skip TTL checks) or update the documentation to match the actual behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +252 to +275
private function is_safe_url( string $url ): bool {
if ( ! wp_http_validate_url( $url ) ) {
return false;
}

$host = wp_parse_url( $url, PHP_URL_HOST );

if ( empty( $host ) ) {
return false;
}

// Resolve hostname to IP for range checking.
// phpcs:ignore WordPress.WP.AlternativeFunctions.net_http_http_request
$ip = gethostbyname( $host );

// Block private, loopback, and reserved IP ranges.
$is_public = filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
);

return false !== $is_public;
}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

The SSRF protection here resolves the host once with gethostbyname() and then requests the original hostname. This is vulnerable to DNS rebinding (host resolves to a public IP during validation but to a private IP during the actual HTTP request). Consider using wp_safe_remote_get() with 'reject_unsafe_urls' => true (and/or validating the resolved IP at request time via pre_http_request/http_request_host_is_external) instead of (or in addition to) manual gethostbyname() checks.

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +67
array( 'value' => 'meta_field', 'label' => 'Match by meta key' ),
array( 'value' => 'post_parent', 'label' => 'Match by WordPress parent post' ),
array( 'value' => 'taxonomy', 'label' => 'Match by taxonomy term' ),
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

Several option labels in the schema are hard-coded strings (e.g. "Match by meta key", "Post ID", etc.) instead of being wrapped in translation functions. Since the rest of the generator UI strings use __(), these labels should be localized as well to keep the editor fully translatable.

Suggested change
array( 'value' => 'meta_field', 'label' => 'Match by meta key' ),
array( 'value' => 'post_parent', 'label' => 'Match by WordPress parent post' ),
array( 'value' => 'taxonomy', 'label' => 'Match by taxonomy term' ),
array( 'value' => 'meta_field', 'label' => __( 'Match by meta key', 'jet-form-builder' ) ),
array( 'value' => 'post_parent', 'label' => __( 'Match by WordPress parent post', 'jet-form-builder' ) ),
array( 'value' => 'taxonomy', 'label' => __( 'Match by taxonomy term', 'jet-form-builder' ) ),

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +69
'value_field' => array(
'type' => 'string',
'default' => 'ID',
'label' => __( 'Option Value', 'jet-form-builder' ),
'control' => 'select',
'options' => array(
array( 'value' => 'ID', 'label' => 'User ID' ),
array( 'value' => 'user_login', 'label' => 'Username (login)' ),
array( 'value' => 'user_email', 'label' => 'Email' ),
array( 'value' => 'user_nicename', 'label' => 'Nicename (slug)' ),
),
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

Schema option labels like "User ID", "Username", "Registration Date" are not wrapped in ()/esc_html(), so they won't be translatable in the editor. Please localize these strings to match the rest of the plugin UI.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

🤖 AI PR Review

Risk level: high

Review

Summary
This PR introduces a large refactor/feature set around option generators: a new Base_V2 generator base with schema support, a Registry for generators, several new generators (Users, REST API, Related Posts), legacy parser/migration helpers, editor-side auto-update support (JS + CSS), and JetEngine macro integrations for auto-update. This is a large and useful change, but it also touches many core code paths and contains a few high-risk issues that must be addressed before merging.

Main issues / required changes

  1. Missing get_schema_for_js method -> fatal risk
  • File: includes/generators/registry.php
  • Problem: Registry::get_schemas_for_js() assumes V2 generators implement get_schema_for_js():
    if ( $generator instanceof Base_V2 ) { $schemas[ $id ] = $generator->get_schema_for_js(); }
    but Base_V2 (includes/generators/base-v2.php) defines get_settings_schema(), get_block_attributes(), parse_settings(), validate_settings(), etc. There is no get_schema_for_js() implemented on Base_V2. This will cause a fatal error when registry is used.
  • Action: add get_schema_for_js() to Base_V2 (or change registry to call get_settings_schema() and transform to the expected format). Ensure the format returned is what tools->get_generator_schemas_for_js() and the editor expect. Add unit coverage.
  1. Server-side endpoint for auto-update generator updates
  • File: modules/option-field/assets/build/auto-update.js and inline code reference
  • Problem: JS posts to /jet-form-builder/v1/generator-update (or uses wpApiSettings.root + '/jet-form-builder/v1/generator-update'). I couldn't find a corresponding REST route implementation in this patch. Without a server-side REST route (and proper permission/nonce checks), auto-update will fail or be insecure.
  • Action: implement the REST endpoint that:
    • Verifies the WP nonce (wp_verify_nonce or REST permission callback using current_user_can / wp_rest_check_user_permissions context) for non-logged-in preview usage (the JS already sends wpApiSettings.nonce). For preview requests the code may need a dedicated preview nonce validation.
    • Validates generator_id and generator settings, uses Registry to resolve generator, sanitizes context values, and returns sanitized options.
    • Protects against abuse (rate limiting, capability checks for admin-only features) and ensures preview mode vs public site mode is handled appropriately.
  1. Unsanitized values returned to JetEngine relations
  • File: compatibility/jet-engine/jet-engine.php -> resolve_relation_macro_source()
  • Problem: resolve_relation_macro_source() returns raw values from $_REQUEST and $GLOBALS without sanitization; upstream JetEngine use may feed that value into queries. This is an injection/validation risk.
  • Action: sanitize/validate returned values depending on expected type (absint() for IDs, sanitize_text_field() for strings, maybe JSON decode for arrays). Also document expected value type in macro registration and cast accordingly. Add tests for macros with malicious input.
  1. Potential SSRF / remote request hardening (REST API generator)
  • File: includes/generators/get-from-rest-api.php
  • Positive: you used wp_http_validate_url() and 'reject_unsafe_urls' => true which is good.
  • Concerns: still possible to leak sensitive network internal resources (depending on server config), or cause DoS with many requests.
  • Action: add caching for responses (transient-based); add optional server-side rate limiting; explicitly disallow private IP ranges if appropriate (or make behavior opt-in). Log errors and expose useful error messages for editors. Document the security model in code comments.
  1. Potential heavy DB queries
  • File: includes/generators/get-from-db.php
  • Problem: SELECT DISTINCT meta_value FROM wp_postmeta WHERE meta_key = %s could be very heavy on large sites. This code now uses DISTINCT and filters empty values which is better, but still can be slow.
  • Action: consider adding an optional limit, indexing advice in code comments, transient caching, or using optimized queries (GROUP BY / LIMIT). Also consider adding a maximum row threshold to bail out and return an informative error message in the editor.
  1. Back-compat / Legacy parsing correctness and migration
  • Files: includes/generators/legacy-parser.php and several generators changed to Base_V2
  • Notes: Good effort to support legacy pipe-delimited formats. However:
    • Ensure the migration path is idempotent and does not unexpectedly override existing migrated attributes.
    • Double-check cases where generator args are stored in different attributes (generator_args, generator_field, calculated_value_from_key) — the fallback logic appears in several places; add unit tests to ensure all patterns are covered.
  1. API / Registry integration edge cases
  • File: includes/classes/tools.php -> get_generator_schemas_for_js()
  • Problem: Tools method calls GeneratorRegistry::instance()->get_schemas_for_js(); that will rely on the registry and generators being available at runtime. Ensure autoloading order is correct and that calling this method in admin/editor context will not attempt to instantiate classes that depend on things not yet loaded (wrap in try/catch is present, good). Also ensure Registry->get_schemas_for_js() handles incomplete generators gracefully (see Calculate field does not work with more complex calculations #1).
  1. Missing or incomplete implementations in this patch
  • I observed incomplete/truncated code in several new/modified files in the diff (create_legacy_schema in registry.php and many large generator files were truncated in the patch). Before merging, ensure all files are complete and pass a full static analysis and unit test run.
  1. WPCS / code style / docblocks
  • Several new methods are well-documented, but some functions and methods are missing @param / @return docblocks or use inline typed signatures inconsistently. Run PHPCS with WPCS rules and fix small style issues.
  1. Tests
  • This change touches many critical features and should include unit/integration tests for:
    • Registry schema export (get_schemas_for_js)
    • Legacy_Parser migration cases
    • generate_with_context() behavior for Get_From_DB, Get_From_Rest_Api, Get_From_Users, Get_Related_Posts
    • resolve_relation_macro_source and macros (sanitization, context precedence)
    • The REST endpoint for generator-update (permission/nonce checks and output format)
    • JS auto-update integration (can be an integration/browser test or mocked endpoint)

Other suggestions / improvements

  • Add caching (transients or an internal cache layer) for expensive generators (DB DISTINCT, remote API) and ensure JS cache invalidation works with the auto-update cacheManager (server and client caches should be independent but consistent).
  • Consider limiting the maximum number of returned options by default to protect editors from rendering thousands of items; provide a warning in the editor if limit exceeds a high threshold.
  • In Base_V2, provide a default get_schema_for_js() implementation that returns a normalized schema object (label, control, options) and keeps backward compatibility. That will make the registry simpler.
  • Validate and sanitize generator settings on server-side before using them in SQL/HTTP requests.

Files I specifically reviewed and why they matter

  • includes/generators/base-v2.php — large new base class; missing get_schema_for_js and possibly other helper methods expected by registry.
  • includes/generators/registry.php — central piece for editor/localization; currently expects methods not present on Base_V2; must be robust.
  • includes/generators/get-from-db.php — DB query performance & sanitization.
  • includes/generators/get-from-rest-api.php — SSRF, error handling, caching, placeholder substitution.
  • includes/generators/get-related-posts.php, get-from-users.php, num-range*, num-range-manual.php — new generator logic and auto-update support.
  • includes/generators/legacy-parser.php — migration path; needs tests.
  • compatibility/jet-engine/jet-engine.php and macro files — integration with JetEngine; sanitize values returned to JetEngine.
  • modules/option-field/assets/build/auto-update.js — client-side auto-update flow relies on server endpoint and expects JSON format; ensure server API matches.

Conclusion
This PR implements a large and important enhancement to the generators and auto-update features. Before merging, please address the high-risk items above (fatal missing method, finalize REST endpoint and permissions, sanitize returned macro values, and add tests). After fixes and tests, re-run static analysis (PHPCS/WPCS), run a performance check for large datasets, and validate JetEngine integrations on a staging site with JetEngine installed.

If you want I can provide a suggested implementation for get_schema_for_js() on Base_V2 and an example REST route handler for generator-update.

Suggested changelog entry

- IMPROVE: Rework option generators — add structured generator schemas (Base_V2), generator registry, new generators (Users, REST API, Related Posts), legacy migration support and JS auto-update integration for cascading fields (JetEngine macros included).

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

🤖 AI PR Review

Risk level: high

Review

Summary

  • This PR adds a new generator v2 system (Base_V2), multiple new generators (users, REST API, related posts), a Generator Registry and Legacy Parser, editor auto-update UI/JS/CSS, JetEngine compatibility macros and a new “update” action button variation. It’s a large, cross-cutting change touching generator architecture, block UI, and JetEngine integration.

Major issues / blockers

  1. Missing method used by Registry -> fatal

    • File: includes/generators/registry.php
    • Problem: get_schemas_for_js() calls $generator->get_schema_for_js() for V2 generators, but Base_V2 (includes/generators/base-v2.php) does not implement get_schema_for_js(). This will trigger a fatal error when Registry::get_schemas_for_js() is called (e.g. from Tools::get_generator_schemas_for_js()).
    • Action: implement get_schema_for_js() on Base_V2 (or change Registry to call get_settings_schema() and get_block_attributes()). Suggested implementation:
      public function get_schema_for_js(): array {
      return array(
      'id' => $this->get_id(),
      'name' => $this->get_name(),
      'schema' => $this->get_settings_schema(),
      'attributes' => $this->get_block_attributes(),
      'supports_auto_update' => $this->supports_auto_update(),
      );
      }
    • Also ensure other expected helper methods used in registry (get_schema_for_js or similar) are either implemented or registry adjusted.
  2. server-side security: REST "generator-update" endpoint verification

    • File: modules/option-field/assets/build/auto-update.js (client) calls POST to /jet-form-builder/v1/generator-update and sets X-WP-Nonce when wpApiSettings.nonce exists.
    • Problem: the PR does not show the REST endpoint implementation. The server handler must always verify the WP nonce (wp_verify_nonce or rest validate) and capability access before using input or performing queries. Without proper verification this is a CSRF/open access risk.
    • Action: ensure the REST endpoint handler checks:
      • current_user_can() if needed (for preview vs public behavior); or allow unauthenticated but verify a request nonce (preview_nonce param is used in JS, but server must validate it) and restrict data returned appropriately.
      • Use wp_json_* and proper input sanitization for context and generator_id.
      • Add rate limiting or transient caching for heavy external calls (Get_From_Rest_Api).
  3. Unsanitized/unsafe values used in JetEngine relation macro resolver

    • File: compatibility/jet-engine/jet-engine.php -> resolve_relation_macro_source()
    • Problem: it returns raw values from $_REQUEST / $GLOBALS / jet_fb_context without sanitization and returns them to JetEngine relations system. JetEngine likely expects numeric post IDs. Passing unsanitized data may cause unexpected behavior or injection points depending on how JetEngine consumes it.
    • Action: sanitize and cast expected types. If object id expected, return absint($value) or false when not numeric. If string allowed, sanitize_text_field(). Document expected return type in comment.
  4. Base_Field_Context_Macro::sanitize_value doesn't sanitize arrays recursively

    • File: compatibility/jet-engine/macros/base-field-context-macro.php
    • Problem: sanitize_value() returns arrays untouched which may allow unsafe data to propagate (e.g. into queries or output).
    • Action: implement recursive sanitization for arrays (walk each scalar with sanitize_text_field or cast numeric where applicable). Example:
      if (is_array($value)) { array_walk_recursive($value, function(&$v){ if (is_string($v)) { $v = sanitize_text_field($v); } }); return $value; }
  5. Potential autoload / instantiation order problems

    • Files: includes/form-manager.php (adds new generator classes), includes/generators/*.php
    • Problem: form-manager.php now instantiates new generators (Get_From_Users, Get_Related_Posts, Get_From_Rest_Api). If the autoloader does not know about new files or these files are not required before instantiation, this will fatal. The rest of the code (Registry) also depends on Plugin::instance()->form being available.
    • Action: verify PSR-4/autoloading includes includes/generators namespace and the plugin bootstrapping order creates Plugin::instance() and registers autoloader before Form_Manager::get_options_generators() is called. Add require_once or ensure autoload config updated if needed.
  6. Input validation / legacy format handling must be hardened

    • Several generator classes accept legacy pipe-delimited strings (generator_field) or arrays via generator_args. Good to keep legacy support, but ensure robust validation/sanitization in every generate()/generate_with_context() implementation. A few classes already sanitize (e.g. Get_From_DB), but double-check these spots:
      • Get_From_Je_Query (compatibility/jet-engine/generators/get-from-je-query.php) — it introduces complex parsing (parse_settings/enrich_legacy_field) and supports auto-update. Verify that query_id/value_field/label_field are sanitized and validated before using in DB or Query_Builder calls.
      • Get_From_Rest_Api — must validate endpoint URL strictly (wp_http_validate_url used) and ensure JSON parse errors, large payloads and slow endpoints are guarded. Consider a response size guard and transient caching.
  7. JetEngine macro classes reading global request state

    • Files: compatibility/jet-engine/macros/auto-update-field-value.php, auto-update-appointment-*.php
    • Problem: macros rely on $GLOBALS['jfb_generator_context'], $_REQUEST, or global variables. This is acceptable for migration, but ordering and scope must be documented. Ensure these values cannot be used to bypass capability checks. Also macro callbacks should return sanitized values (Base_Field_Context_Macro changes above will help).
  8. JS: client-side caching & request behavior

    • File: modules/option-field/assets/build/auto-update.js
    • Good: complex, includes caching, abort controllers, X-WP-Nonce usage. Manual review found it handles abort and caching.
    • Recommend: ensure server-side responses are cacheable (with TTL) and evidence of handling errors is surfaced gracefully. Add server-side rate limiting or transient caching for external REST requests.

Minor / style / BC considerations

  • action button variation: includes/blocks/button-types/button-update.php and its registration in includes/blocks/types/action-button.php are fine and include conflict avoidance with JFB_Update_Field plugin.
  • Many docblocks added — follow WPCS formatting; ensure phpcs passes. e.g. return docblocks should use proper type hints (int|string|array) consistent with plugin minimum PHP.
  • includes/generators/registry.php: create_legacy_schema() is partially implemented in patch; ensure it covers all legacy generators used in create_legacy_schema to avoid undefined index errors.
  • Includes: ensure new classes (Registry, Base_V2, Legacy_Parser) are covered by unit tests — particularly parsing legacy formats and schema export.

Tests needed

  • Unit/integration tests for Registry::get_schemas_for_js() to ensure no fatal and correct output for legacy and V2 generators.
  • REST endpoint tests for generator-update: nonce/permission checks, invalid generator_id, context data, response format and error handling.
  • End-to-end test for auto-update: verify client can call endpoint, server returns sanitized options, and options update in UI.

Suggested concrete code changes (summary)

  • Implement Base_V2::get_schema_for_js() and any other helper methods expected by Registry.
  • Harden Base_Field_Context_Macro::sanitize_value() to sanitize arrays recursively.
  • In compatibility/jet-engine/jet-engine.php::resolve_relation_macro_source(), sanitize and cast returned value (e.g. $value = $_REQUEST[$request_key]; return is_numeric($value) ? absint($value) : sanitize_text_field($value); or return false when non-numeric if JetEngine expects IDs).
  • Ensure REST endpoint verifies nonce and permissions and uses wp_validate_boolean / sanitize_text_field on inputs. Add transient caching for heavy external requests.
  • Add fallback / defensive checks in Registry::load_generators() for Plugin::instance()->form existence (you already do partially), and ensure generators are autoloadable.

Overall assessment

  • The feature set is valuable (structured generator schemas + auto-update), but the PR introduces cross-cutting changes that must be hardened before merge to avoid fatal errors and security issues. The missing get_schema_for_js is a showstopper.

If you want I can draft the exact code snippets for the fixes above (Base_V2::get_schema_for_js, recursive sanitize_value, resolve_relation_macro_source sanitization, and an example REST permission check).

Suggested changelog entry

- IMPROVE: Add structured generators (Base_V2), new generators and auto-update/cascading fields with JetEngine compatibility and editor UI improvements

@Gawuww Gawuww merged commit 01bc56d into release/3.6.0 Apr 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants