Skip to content

Conversation

@arifulhoque7
Copy link
Contributor

@arifulhoque7 arifulhoque7 commented Nov 24, 2025

fix: correctly detect ACF taxonomies by checking post_content

The wpuf_is_acf_taxonomy() function was incorrectly checking the post_name
field when querying for ACF-registered taxonomies. ACF stores the actual
taxonomy slug in the serialized post_content under the 'taxonomy' key,
while post_name contains ACF's internal identifier (e.g., 'taxonomy_69242380c35d7').

Changes:

  • Modified database query to fetch all acf-taxonomy posts
  • Added loop to unserialize post_content and check taxonomy key
  • ACF taxonomies now properly detected as free fields
  • Removed PRO badge from ACF taxonomy fields in form builder

This ensures ACF custom taxonomies are available in the free version
without requiring PRO upgrade, as they are user-created content.

Close #1672

Summary by CodeRabbit

  • New Features

    • ACF and other non-built-in taxonomies are now included in the free taxonomy set, so they render and can be selected without Pro.
    • Taxonomy visibility/filtering for non‑Pro users updated to preserve these taxonomies when listing or filtering fields.
    • Added utilities to build and expose the free-taxonomy list used by the UI.
  • Chores

    • Minor array/format refactors for consistency.

✏️ Tip: You can customize this high-level summary in your review settings.

@arifulhoque7 arifulhoque7 requested a review from sapayth November 24, 2025 09:54
@arifulhoque7 arifulhoque7 self-assigned this Nov 24, 2025
@arifulhoque7 arifulhoque7 added needs: testing needs: dev review This PR needs review by a developer labels Nov 24, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

Replaces static built-in taxonomy checks with a dynamic free-taxonomy set (built-ins + eligible custom/ACF taxonomies) and adds helpers to build that set; updated admin, field, and render logic to consult the dynamic set when Pro is inactive.

Changes

Cohort / File(s) Summary
Helper functions
wpuf-functions.php
Added wpuf_get_taxonomy_post_types($taxonomy_name) and wpuf_get_free_taxonomies(); minor array syntax modernizations.
Admin form builder updates
includes/Admin/Forms/Admin_Form_Builder.php
Replaced hard-coded builtin taxonomy list with wpuf_get_free_taxonomies() in filter_pro_taxonomy_fields(), has_filtered_taxonomies(), and get_hidden_taxonomy_ids() to determine free vs. gated taxonomies when Pro is inactive.
Field updates
includes/Fields/Form_Field_Post_Taxonomy.php
is_pro() and render() now consult wpuf_get_free_taxonomies() so eligible custom/ACF taxonomies are treated as free when Pro is inactive.
Form renderer
includes/Render_Form.php
Taxonomy gating and rendering now use wpuf_get_free_taxonomies() for pro-gating decisions; pro-active path unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • sapayth

Poem

🐰 I hopped through taxonomies, light as a breeze,

Built-ins and ACF now share the same keys.
Forms hum softly, fields open wide,
Pro's gate shifts gently — welcome inside. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions detecting ACF taxonomies by checking post_content, but the actual changes focus on making ACF taxonomies available as free features by using wpuf_get_free_taxonomies(). Update the title to accurately reflect the main change, such as 'feat: add ACF taxonomies to free version taxonomy detection' or 'fix: treat ACF taxonomies as free features'.
Linked Issues check ⚠️ Warning The PR addresses ACF taxonomy detection for the free version, but issue #1672 requires implementing Selection Type filtering (Exclude, Include, Child of) for taxonomy fields, which is not implemented in these changes. Either implement the Selection Type filtering requirements from issue #1672, or clarify that this PR only addresses ACF taxonomy detection and a separate PR will handle Selection Type filtering.
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Out of Scope Changes check ✅ Passed All changes are focused on detecting ACF taxonomies as free features through wpuf_get_free_taxonomies() and related adjustments, with no unrelated modifications detected.
✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
includes/Admin/Forms/Admin_Form_Builder.php (2)

173-176: Optimize repeated calls to wpuf_get_free_taxonomies().

The function wpuf_get_free_taxonomies() is called three times in quick succession (once per method call on lines 174, 175, and 176). According to the PR description, this function queries the database to detect ACF taxonomies by checking post_content, and it also calls get_taxonomies() to retrieve all non-built-in taxonomies.

Consider caching the result to avoid redundant database queries.

Apply this diff to cache the result:

 // Filter pro taxonomy fields and check if any were removed
 $original_fields = wpuf_get_form_fields( $post->ID );
-$filtered_fields = $this->filter_pro_taxonomy_fields( $original_fields );
-$has_hidden_taxonomies = $this->has_filtered_taxonomies( $original_fields, $filtered_fields );
-$hidden_taxonomy_ids = $this->get_hidden_taxonomy_ids( $original_fields, $filtered_fields );
+$free_taxonomies = wpuf_get_free_taxonomies(); // Cache the result
+$filtered_fields = $this->filter_pro_taxonomy_fields( $original_fields, $free_taxonomies );
+$has_hidden_taxonomies = $this->has_filtered_taxonomies( $original_fields, $filtered_fields, $free_taxonomies );
+$hidden_taxonomy_ids = $this->get_hidden_taxonomy_ids( $original_fields, $filtered_fields, $free_taxonomies );

Then update the three method signatures to accept an optional $free_taxonomies parameter:

protected function filter_pro_taxonomy_fields( $form_fields, $free_taxonomies = null ) {
    // If pro is active, return all fields
    if ( wpuf_is_pro_active() ) {
        return $form_fields;
    }

    // Get free taxonomies (built-in + ACF taxonomies)
    if ( null === $free_taxonomies ) {
        $free_taxonomies = wpuf_get_free_taxonomies();
    }
    
    // ... rest of the method
}

Apply the same pattern to has_filtered_taxonomies and get_hidden_taxonomy_ids.


173-176: Add caching to wpuf_is_acf_taxonomy() to prevent redundant database queries.

The implementation at lines 5720-5770 in wpuf-functions.php executes a full database query on every call to fetch all ACF taxonomy posts without caching. Combined with wpuf_get_free_taxonomies() calling this function once per non-builtin taxonomy (line 5804), and Admin_Form_Builder.php calling wpuf_get_free_taxonomies() at least three times (lines 521, 553, 595), this creates redundant queries for identical data in a single admin page load.

Consider implementing either:

  • Static variable memoization within wpuf_is_acf_taxonomy() to cache results during execution
  • Transient caching (similar to the PRO_FIELD_ASSETS pattern at lines 485-496) to cache ACF taxonomy mappings across requests
🧹 Nitpick comments (1)
wpuf-functions.php (1)

5708-5781: ACF taxonomy detection works but could use caching and a simpler fallback

The new wpuf_is_acf_taxonomy() and wpuf_get_free_taxonomies() look functionally correct and match the PR intent (treat built‑ins + ACF taxonomies as free). Two improvement points:

  1. Avoid repeated full‑table scans for ACF taxonomies

wpuf_get_free_taxonomies() iterates over every non‑builtin taxonomy and calls wpuf_is_acf_taxonomy(), which in turn runs the same SELECT post_content FROM {$wpdb->posts} WHERE post_type = 'acf-taxonomy' ... on every call. On sites with many custom taxonomies or ACF taxonomy definitions this can become unnecessarily expensive per request.

You can cache the discovered ACF taxonomy slugs once per request and reuse them:

-if ( ! function_exists( 'wpuf_is_acf_taxonomy' ) ) {
-    function wpuf_is_acf_taxonomy( $taxonomy_name ) {
-        // If taxonomy doesn't exist, it can't be an ACF taxonomy
-        if ( ! taxonomy_exists( $taxonomy_name ) ) {
-            return false;
-        }
-
-        // Get the taxonomy object
-        $taxonomy = get_taxonomy( $taxonomy_name );
-        
-        if ( ! $taxonomy ) {
-            return false;
-        }
-
-        // Check if ACF is active
-        if ( ! class_exists( 'acf' ) ) {
-            return false;
-        }
-
-        global $wpdb;
-
-        $acf_taxonomies = $wpdb->get_results(
-            "SELECT post_content FROM {$wpdb->posts}
-            WHERE post_type = 'acf-taxonomy'
-            AND post_status = 'publish'"
-        );
-
-        if ( ! empty( $acf_taxonomies ) ) {
-            foreach ( $acf_taxonomies as $acf_tax ) {
-                $config = maybe_unserialize( $acf_tax->post_content );
-
-                if ( is_array( $config ) && isset( $config['taxonomy'] ) && $config['taxonomy'] === $taxonomy_name ) {
-                    return true;
-                }
-            }
-        }
-
-        // Additional check block…
-        if ( isset( $taxonomy->acf ) || isset( $taxonomy->_builtin ) && ! $taxonomy->_builtin ) {
-            if ( function_exists( 'acf_get_internal_post_type' ) ) {
-                $internal_types = acf_get_internal_post_type( 'acf-taxonomy', 'names' );
-                if ( is_array( $internal_types ) && in_array( $taxonomy_name, $internal_types, true ) ) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-}
+if ( ! function_exists( 'wpuf_is_acf_taxonomy' ) ) {
+    function wpuf_is_acf_taxonomy( $taxonomy_name ) {
+        static $acf_taxonomy_slugs = null;
+
+        if ( ! taxonomy_exists( $taxonomy_name ) || ! class_exists( 'acf' ) ) {
+            return false;
+        }
+
+        if ( null === $acf_taxonomy_slugs ) {
+            global $wpdb;
+
+            $acf_taxonomy_slugs = [];
+
+            $acf_posts = $wpdb->get_results(
+                "SELECT post_content FROM {$wpdb->posts}
+                 WHERE post_type = 'acf-taxonomy'
+                 AND post_status = 'publish'"
+            );
+
+            foreach ( (array) $acf_posts as $acf_post ) {
+                $config = maybe_unserialize( $acf_post->post_content );
+
+                if ( is_array( $config ) && ! empty( $config['taxonomy'] ) ) {
+                    $acf_taxonomy_slugs[] = $config['taxonomy'];
+                }
+            }
+
+            $acf_taxonomy_slugs = array_unique( $acf_taxonomy_slugs );
+        }
+
+        return in_array( $taxonomy_name, $acf_taxonomy_slugs, true );
+    }
+}

You can similarly cache the result of wpuf_get_free_taxonomies() if you expect it to be called multiple times in a single request:

-if ( ! function_exists( 'wpuf_get_free_taxonomies' ) ) {
-    function wpuf_get_free_taxonomies() {
+if ( ! function_exists( 'wpuf_get_free_taxonomies' ) ) {
+    function wpuf_get_free_taxonomies() {
+        static $cached = null;
+
+        if ( null !== $cached ) {
+            return $cached;
+        }
@@
-        // Allow filtering to add more free taxonomies
-        $free_taxonomies = apply_filters( 'wpuf_free_taxonomies', $free_taxonomies );
+        // Allow filtering to add more free taxonomies
+        $free_taxonomies = apply_filters( 'wpuf_free_taxonomies', $free_taxonomies );
@@
-        return $free_taxonomies;
+        $cached = $free_taxonomies;
+
+        return $cached;
     }
 }
  1. Questionable fallback using acf_get_internal_post_type

The current fallback compares $taxonomy_name against $internal_types returned by acf_get_internal_post_type( 'acf-taxonomy', 'names' ). Since $taxonomy_name is a taxonomy slug (e.g., genre), and acf_get_internal_post_type returns internal post type identifiers, this condition is unlikely ever to be true. With the serialized‑config check in place, you can safely drop this branch, or at least document a concrete scenario where taxonomy slugs are expected to match those internal identifiers.

  1. Minor docblock nit

Both new docblocks use @since WPUF_SINCE; consider replacing that placeholder with the actual version string before release to keep API docs accurate.

Also applies to: 5792-5811

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47b6597 and bb1fc6b.

📒 Files selected for processing (4)
  • includes/Admin/Forms/Admin_Form_Builder.php (5 hunks)
  • includes/Fields/Form_Field_Post_Taxonomy.php (2 hunks)
  • includes/Render_Form.php (2 hunks)
  • wpuf-functions.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
includes/Admin/Forms/Admin_Form_Builder.php (1)
wpuf-functions.php (1)
  • wpuf_get_free_taxonomies (5793-5811)
wpuf-functions.php (1)
includes/Render_Form.php (1)
  • taxonomy (1354-1518)
includes/Fields/Form_Field_Post_Taxonomy.php (1)
wpuf-functions.php (2)
  • wpuf_get_free_taxonomies (5793-5811)
  • wpuf_is_pro_active (4988-4990)
includes/Render_Form.php (1)
wpuf-functions.php (2)
  • wpuf_get_free_taxonomies (5793-5811)
  • wpuf_is_pro_active (4988-4990)
🔇 Additional comments (4)
includes/Render_Form.php (1)

1332-1346: Gating logic now correctly uses the shared free-taxonomy helper

Using wpuf_get_free_taxonomies() in both is_taxonomy_field_gated() and taxonomy() aligns frontend gating with the new central helper (built‑in + ACF taxonomies) and with the form‑field class, so ACF taxonomies won’t be blocked in the free version while still respecting Pro checks. No issues spotted in the control flow.

Also applies to: 1355-1359

includes/Fields/Form_Field_Post_Taxonomy.php (1)

49-59: Post‑taxonomy field now shares a single source of truth for free vs pro gating

is_pro() and render() both delegate to wpuf_get_free_taxonomies() plus wpuf_is_pro_active(), so the builder/field class and frontend renderer gate taxonomies the same way (built‑ins + ACF allowed in free; other custom taxonomies hidden when Pro is inactive). This matches the PR’s goal and looks logically sound.

Also applies to: 71-79

includes/Admin/Forms/Admin_Form_Builder.php (2)

514-538: Logic looks correct for filtering taxonomy fields.

The dynamic taxonomy detection properly handles built-in and ACF taxonomies by using wpuf_get_free_taxonomies(). The filtering logic correctly retains free taxonomies while excluding custom non-ACF taxonomies when PRO is inactive.


587-617: Logic correctly identifies hidden taxonomy field IDs.

The method properly extracts IDs of custom taxonomy fields that were filtered out, enabling client-side preservation of these fields when PRO is activated later.

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify the fallback to taxonomy key is necessary.

Line 571 extracts the taxonomy slug with $f['name'] ?? ($f['taxonomy'] ?? null), falling back to the taxonomy key. However, the other methods (filter_pro_taxonomy_fields and get_hidden_taxonomy_ids) only check $field['name'].

Ensure this fallback is intentional and that taxonomy fields can indeed have the slug under either key, or consider aligning the extraction logic across all three methods for consistency.


🏁 Script executed:

#!/bin/bash
# Description: Search for taxonomy field structures to verify which keys are used for the taxonomy slug

# Search for where taxonomy fields are created/structured
rg -n -C3 'input_type.*taxonomy|taxonomy.*input_type' --type=php

# Look for assignments to taxonomy field 'name' or 'taxonomy' keys
ast-grep --pattern $'$field[\'name\'] = $_'
ast-grep --pattern $'$field[\'taxonomy\'] = $_'

Length of output: 16231



🏁 Script executed:

#!/bin/bash
# Search for where 'taxonomy' key is actually assigned to fields
rg -n -C2 "\['taxonomy'\]\s*=" --type=php | head -50

# Also search for field definitions that might have taxonomy key
rg -n "taxonomy.*=>|'taxonomy'" --type=php -C2 | grep -E "\['taxonomy'\]|'taxonomy'\s*=>" | head -30

Length of output: 4029



🏁 Script executed:

#!/bin/bash
# Search for how nested/column fields are constructed to see which keys they preserve
rg -n -B5 -A5 "column_field|inner.*field" includes/Admin/Forms/Admin_Form_Builder.php | head -60

# Also check the actual field structure in the stack-based traversal
rg -n -B10 -A10 "has_filtered_taxonomies" includes/Admin/Forms/Admin_Form_Builder.php

Length of output: 3215


Align the taxonomy slug extraction with other methods for consistency.

Line 571 extracts the slug with a fallback to $f['taxonomy'], but filter_pro_taxonomy_fields (line 528) and get_hidden_taxonomy_ids (line 607) only check $field['name']. Since all observed field definitions consistently populate the name key, the fallback appears unnecessary. Either document why the taxonomy key fallback is needed for nested fields, or remove it to align with the other methods.

🤖 Prompt for AI Agents
In includes/Admin/Forms/Admin_Form_Builder.php around lines 570 to 572, the code
falls back to $f['taxonomy'] when extracting the slug but other methods
(filter_pro_taxonomy_fields at line 528 and get_hidden_taxonomy_ids at line 607)
only use $field['name']; remove the unnecessary fallback and read the slug
strictly from $f['name'] (e.g., $slug = $f['name'] ?? null), update the
conditional accordingly, and add a brief inline comment if you believe nested
fields require a different key so future readers know why only 'name' is used.

@Rubaiyat-E-Mohammad Rubaiyat-E-Mohammad added QA Approved This PR is approved by the QA team and removed needs: testing labels Nov 25, 2025
Introduced wpuf_get_taxonomy_post_types() to retrieve post types associated with a taxonomy. Updated wpuf_get_free_taxonomies() to include taxonomies linked to 'post' or 'page' post types in the free list, improving extensibility and compatibility with custom and ACF taxonomies.
Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (1)
wpuf-functions.php (1)

5816-5866: wpuf_get_free_taxonomies broadens “free” scope; confirm intent and consider filter + naming tweaks

A few points on this helper:

  1. Scope of “free” taxonomies is broader than just ACF
    The docblock says this “includes built-in taxonomies and ACF-registered taxonomies,” but the logic adds any taxonomy whose object_type includes post or page:

    $all_taxonomies   = get_taxonomies( array(), 'names' );
    ...
    $post_types       = wpuf_get_taxonomy_post_types( $taxonomy_name );
    $allowed_post_types = array( 'post', 'page' );
    ...
    if ( in_array( $post_type, $allowed_post_types, true ) ) {
        $free_taxonomies[] = $taxonomy_name;
    }

    That means all non‑ACF custom taxonomies registered for post or page also become “free”, not just those created via ACF. If your Pro gating model is “only built‑ins + ACF taxonomies are free,” this implementation relaxes it further.

    If the broader behavior is intentional, I’d update the comment to say so explicitly. If not, you likely want to either:

    • Use wpuf_is_acf_taxonomy( $taxonomy_name ) in this loop to selectively add only ACF‑managed taxonomies, or
    • Restrict by a more precise rule (e.g., a dedicated allow‑list filter just for ACF taxonomies).
  2. Commented-out filter removes an extension point
    The commented line:

    // $free_taxonomies = apply_filters( 'wpuf_free_taxonomies', $free_taxonomies );

    suggests this was meant to be pluggable. If Pro gating is not considered a hard security boundary, re-enabling this filter (and documenting that it controls free/Pro taxonomy availability) would make the behavior easier to customize without patching core.

  3. Minor consistency / naming nit
    $has_allowed_type could be renamed to $has_allowed_post_type for clarity, but that’s purely cosmetic.

If the goal is exactly “built‑in + ACF taxonomies are free, everything else stays Pro‑only”, I’d expect something closer to:

-        foreach ( $all_taxonomies as $taxonomy_name ) {
+        foreach ( $all_taxonomies as $taxonomy_name ) {
             if ( in_array( $taxonomy_name, $free_taxonomies, true ) ) {
                 continue;
             }

-            $post_types = wpuf_get_taxonomy_post_types( $taxonomy_name );
-
-            if ( ! empty( $post_types ) ) {
-                $allowed_post_types = array( 'post', 'page' );
-                $has_allowed_type = false;
-
-                foreach ( $post_types as $post_type ) {
-                    if ( in_array( $post_type, $allowed_post_types, true ) ) {
-                        $has_allowed_type = true;
-                        break;
-                    }
-                }
-
-                if ( $has_allowed_type ) {
-                    $free_taxonomies[] = $taxonomy_name;
-                }
-            }
+            // Only treat ACF-managed taxonomies as free in addition to built-ins.
+            if ( wpuf_is_acf_taxonomy( $taxonomy_name ) ) {
+                $free_taxonomies[] = $taxonomy_name;
+            }
         }

Then, if you still want to include all post/page taxonomies, you could do that via a filter:

return apply_filters( 'wpuf_free_taxonomies', array_unique( $free_taxonomies ) );

Given this function directly controls free vs Pro availability, please confirm with product/PM whether the expanded behavior (all post/page taxonomies being free) is intentional before shipping.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb1fc6b and 62bdbec.

📒 Files selected for processing (1)
  • wpuf-functions.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
wpuf-functions.php (3)
includes/Render_Form.php (1)
  • taxonomy (1354-1518)
admin/template-post.php (1)
  • taxonomy (122-237)
includes/Admin/Forms/Template_Post.php (1)
  • taxonomy (130-280)
🔇 Additional comments (1)
wpuf-functions.php (1)

5783-5814: Helper wpuf_get_taxonomy_post_types looks correct and safe

This helper cleanly wraps get_taxonomy()->object_type, returning an empty array when the taxonomy doesn’t exist or the object is missing. That defensive behavior is appropriate for use in feature‑gating logic.

No changes needed here.

Comment on lines 5708 to 5781
/**
* Check if a taxonomy is registered by Advanced Custom Fields (ACF)
*
* ACF taxonomies should be available in the free version as they are user-created
* via ACF, not plugin-specific custom taxonomies.
*
* @since WPUF_SINCE
*
* @param string $taxonomy_name The taxonomy name to check
* @return bool True if taxonomy is registered by ACF, false otherwise
*/
if ( ! function_exists( 'wpuf_is_acf_taxonomy' ) ) {
function wpuf_is_acf_taxonomy( $taxonomy_name ) {
// If taxonomy doesn't exist, it can't be an ACF taxonomy
if ( ! taxonomy_exists( $taxonomy_name ) ) {
return false;
}

// Get the taxonomy object
$taxonomy = get_taxonomy( $taxonomy_name );

if ( ! $taxonomy ) {
return false;
}

// ACF taxonomies typically have these characteristics:
// 1. They are not built-in (_builtin = false)
// 2. They are registered by ACF (check for ACF-specific properties)

// Check if ACF is active
if ( ! class_exists( 'acf' ) ) {
return false;
}

// ACF stores taxonomy configuration in the database
// Check if there's an ACF post type that registered this taxonomy
global $wpdb;

// ACF saves custom taxonomies as posts of type 'acf-taxonomy'
// The taxonomy slug is stored in the serialized post_content, not post_name
// post_name is ACF's internal key like 'taxonomy_69242380c35d7'
$acf_taxonomies = $wpdb->get_results(
"SELECT post_content FROM {$wpdb->posts}
WHERE post_type = 'acf-taxonomy'
AND post_status = 'publish'"
);

if ( ! empty( $acf_taxonomies ) ) {
foreach ( $acf_taxonomies as $acf_tax ) {
// ACF stores the taxonomy configuration as serialized data
$config = maybe_unserialize( $acf_tax->post_content );

// Check if the taxonomy key matches our taxonomy name
if ( is_array( $config ) && isset( $config['taxonomy'] ) && $config['taxonomy'] === $taxonomy_name ) {
return true;
}
}
}

// Additional check: ACF taxonomies often have 'acf' in their labels or registration
// Check if the taxonomy object has ACF-specific metadata
if ( isset( $taxonomy->acf ) || isset( $taxonomy->_builtin ) && ! $taxonomy->_builtin ) {
// Check if registered via ACF by looking for ACF functions
if ( function_exists( 'acf_get_internal_post_type' ) ) {
$internal_types = acf_get_internal_post_type( 'acf-taxonomy', 'names' );
if ( is_array( $internal_types ) && in_array( $taxonomy_name, $internal_types, true ) ) {
return true;
}
}
}

return false;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

ACF Advanced Custom Fields class bootstrap activation check

💡 Result:

Short answer — use plugin checks first, and fall back to class/function checks. Example (best practice per ACF docs):

  • In admin or after including plugin.php:
    if ( is_plugin_active( 'advanced-custom-fields-pro/acf.php' ) || is_plugin_active( 'advanced-custom-fields/acf.php' ) ) {
    // ACF (or ACF Pro) is active — do nothing / prefer site plugin.
    }

  • If you need to detect at runtime (front-end or bundled ACF fallback), use class_exists() / function_exists():
    if ( class_exists( 'acf' ) || class_exists( 'acf_pro' ) || function_exists( 'acf' ) ) {
    // ACF is available.
    } else {
    // Include/bundle ACF safely (define path, include acf.php, set acf/settings/url, hide admin UI, etc.).
    }

References:

  • ACF official guide: including/bundling and plugin detection. [1]
  • Community guidance (class_exists / is_plugin_active usage). [2]

Sources:
[1] Advanced Custom Fields — How to Include ACF PRO in a Plugin or Theme.
[2] WordPress StackExchange / ACF inclusion examples.


🌐 Web query:

acf_get_internal_post_type ACF function signature documentation

💡 Result:

Signature (ACF ≥6.1):

acf_get_internal_post_type( int|string $id, string $post_type ) : array|false

  • Description: Returns an ACF internal post type as an array, or false on failure. [1]

Source: ACF Internal Post Type functions reference. [1]


🏁 Script executed:

# Search the codebase for calls to wpuf_is_acf_taxonomy to assess call frequency
rg "wpuf_is_acf_taxonomy" --type php -B 2 -A 2

Repository: weDevsOfficial/wp-user-frontend

Length of output: 206


Tighten ACF detection and remove the broken fallback heuristic

The core DB-based detection of ACF taxonomies is correct, but two issues need fixing:

  1. ACF detection should use a fallback chain
    The current check class_exists( 'acf' ) is insufficient. Per ACF best practice, use a fallback chain to handle different ACF versions and loading contexts:
-        if ( ! class_exists( 'acf' ) ) {
+        if ( ! class_exists( 'acf' ) && ! class_exists( 'acf_pro' ) && ! function_exists( 'acf' ) ) {
             return false;
         }
  1. Remove the broken acf_get_internal_post_type() fallback block
    The fallback block calling acf_get_internal_post_type( 'acf-taxonomy', 'names' ) is incorrect. Per ACF docs, acf_get_internal_post_type( int|string $id, string $post_type ) : array|false accepts an $id and $post_type parameter and returns a single post type object (or false), not a list of taxonomy slugs. The current call won't return what the code expects, and the in_array() check will always fail.

Since the preceding DB query already correctly identifies ACF taxonomies, remove the broken heuristic entirely:

-        // Additional check: ACF taxonomies often have 'acf' in their labels or registration
-        // Check if the taxonomy object has ACF-specific metadata
-        if ( isset( $taxonomy->acf ) || isset( $taxonomy->_builtin ) && ! $taxonomy->_builtin ) {
-            // Check if registered via ACF by looking for ACF functions
-            if ( function_exists( 'acf_get_internal_post_type' ) ) {
-                $internal_types = acf_get_internal_post_type( 'acf-taxonomy', 'names' );
-                if ( is_array( $internal_types ) && in_array( $taxonomy_name, $internal_types, true ) ) {
-                    return true;
-                }
-            }
-        }
  1. Performance: cache ACF taxonomy slugs across calls (optional)
    Every call runs a full DB query and unserializes all ACF taxonomy configs. For repeated calls within a request, a static cache improves performance with minimal overhead:
-        $acf_taxonomies = $wpdb->get_results(
-            "SELECT post_content FROM {$wpdb->posts}
-            WHERE post_type = 'acf-taxonomy'
-            AND post_status = 'publish'"
-        );
-
-        if ( ! empty( $acf_taxonomies ) ) {
-            foreach ( $acf_taxonomies as $acf_tax ) {
-                // ACF stores the taxonomy configuration as serialized data
-                $config = maybe_unserialize( $acf_tax->post_content );
-
-                // Check if the taxonomy key matches our taxonomy name
-                if ( is_array( $config ) && isset( $config['taxonomy'] ) && $config['taxonomy'] === $taxonomy_name ) {
-                    return true;
-                }
-            }
-        }
+        static $acf_taxonomy_slugs = null;
+
+        if ( null === $acf_taxonomy_slugs ) {
+            $acf_taxonomy_slugs = [];
+
+            $acf_posts = $wpdb->get_results(
+                "SELECT post_content FROM {$wpdb->posts}
+                 WHERE post_type = 'acf-taxonomy'
+                 AND post_status = 'publish'"
+            );
+
+            foreach ( $acf_posts as $acf_post ) {
+                $config = maybe_unserialize( $acf_post->post_content );
+
+                if ( is_array( $config ) && ! empty( $config['taxonomy'] ) ) {
+                    $acf_taxonomy_slugs[] = $config['taxonomy'];
+                }
+            }
+        }
+
+        if ( in_array( $taxonomy_name, $acf_taxonomy_slugs, true ) ) {
+            return true;
+        }
🤖 Prompt for AI Agents
In wpuf-functions.php around lines 5708 to 5781, tighten ACF detection by
replacing the simple class_exists('acf') check with a fallback chain that checks
for acf(), function_exists('acf_get_instance'), and class_exists('ACF'), then
remove the broken acf_get_internal_post_type() fallback block entirely (it
returns a single post object or false, not an array of taxonomy slugs), and
implement a static cache for the DB-driven discovery of ACF taxonomies so the
query + maybe_unserialize work only once per request; ensure the DB loop remains
the primary detector and short-circuits true when a matching config['taxonomy']
=== $taxonomy_name is found, otherwise return false.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
wpuf-functions.php (2)

5821-5831: Remove the broken acf_get_internal_post_type() fallback block.

The fallback block calling acf_get_internal_post_type( 'acf-taxonomy', 'names' ) is incorrect. Per ACF docs, acf_get_internal_post_type( int|string $id, string $post_type ) accepts an $id and $post_type parameter and returns a single post type object (or false), not a list of taxonomy slugs. The current call won't return what the code expects, and the in_array() check will always fail.

Since the preceding DB query (lines 5803-5819) already correctly identifies ACF taxonomies, remove this broken heuristic entirely:

-        // Additional check: ACF taxonomies often have 'acf' in their labels or registration
-        // Check if the taxonomy object has ACF-specific metadata
-        if ( isset( $taxonomy->acf ) || isset( $taxonomy->_builtin ) && ! $taxonomy->_builtin ) {
-            // Check if registered via ACF by looking for ACF functions
-            if ( function_exists( 'acf_get_internal_post_type' ) ) {
-                $internal_types = acf_get_internal_post_type( 'acf-taxonomy', 'names' );
-                if ( is_array( $internal_types ) && in_array( $taxonomy_name, $internal_types, true ) ) {
-                    return true;
-                }
-            }
-        }
-
         return false;
     }
 }

Based on past review comments and ACF function signature documentation.


5792-5792: Strengthen ACF detection with fallback chain.

The current check class_exists( 'acf' ) is insufficient. Per ACF best practice, use a fallback chain to handle different ACF versions and loading contexts:

-        if ( ! class_exists( 'acf' ) ) {
+        if ( ! class_exists( 'acf' ) && ! class_exists( 'acf_pro' ) && ! function_exists( 'acf' ) ) {
             return false;
         }

Based on past review comments and ACF documentation.

🧹 Nitpick comments (2)
wpuf-functions.php (2)

5803-5819: Consider caching ACF taxonomy slugs across calls (optional).

Every call runs a full DB query and unserializes all ACF taxonomy configs. For repeated calls within a request, a static cache improves performance with minimal overhead:

+        static $acf_taxonomy_slugs = null;
+
+        if ( null === $acf_taxonomy_slugs ) {
+            $acf_taxonomy_slugs = [];
+
-        $acf_taxonomies = $wpdb->get_results(
-            "SELECT post_content FROM {$wpdb->posts}
-            WHERE post_type = 'acf-taxonomy'
-            AND post_status = 'publish'"
-        );
-
-        if ( ! empty( $acf_taxonomies ) ) {
-            foreach ( $acf_taxonomies as $acf_tax ) {
-                // ACF stores the taxonomy configuration as serialized data
-                $config = maybe_unserialize( $acf_tax->post_content );
-
-                // Check if the taxonomy key matches our taxonomy name
-                if ( is_array( $config ) && isset( $config['taxonomy'] ) && $config['taxonomy'] === $taxonomy_name ) {
-                    return true;
-                }
-            }
+            $acf_posts = $wpdb->get_results(
+                "SELECT post_content FROM {$wpdb->posts}
+                 WHERE post_type = 'acf-taxonomy'
+                 AND post_status = 'publish'"
+            );
+
+            foreach ( $acf_posts as $acf_post ) {
+                $config = maybe_unserialize( $acf_post->post_content );
+
+                if ( is_array( $config ) && ! empty( $config['taxonomy'] ) ) {
+                    $acf_taxonomy_slugs[] = $config['taxonomy'];
+                }
+            }
+        }
+
+        if ( in_array( $taxonomy_name, $acf_taxonomy_slugs, true ) ) {
+            return true;
         }

Based on past review comments.


5885-5885: Clarify or remove commented filter.

The filter wpuf_free_taxonomies is commented out. If this filter is intentionally disabled, add a comment explaining why. If it should be enabled, uncomment it:

-        // Allow filtering to add more free taxonomies
-        //$free_taxonomies = apply_filters( 'wpuf_free_taxonomies', $free_taxonomies );
+        // Allow filtering to add more free taxonomies
+        $free_taxonomies = apply_filters( 'wpuf_free_taxonomies', $free_taxonomies );

Or if intentionally disabled:

-        // Allow filtering to add more free taxonomies
-        //$free_taxonomies = apply_filters( 'wpuf_free_taxonomies', $free_taxonomies );
+        // Filter intentionally disabled to prevent manual addition of premium taxonomies
+        // $free_taxonomies = apply_filters( 'wpuf_free_taxonomies', $free_taxonomies );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62bdbec and a95e20f.

📒 Files selected for processing (2)
  • includes/Render_Form.php (2 hunks)
  • wpuf-functions.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • includes/Render_Form.php
🧰 Additional context used
🧬 Code graph analysis (1)
wpuf-functions.php (1)
includes/Render_Form.php (1)
  • taxonomy (1354-1518)
🔇 Additional comments (1)
wpuf-functions.php (1)

5847-5868: LGTM!

The function correctly retrieves post types associated with a taxonomy, with proper validation and fallback handling.

arifulhoque7 and others added 2 commits December 4, 2025 15:48
Deleted the wpuf_is_acf_taxonomy() function and related logic for identifying ACF-registered taxonomies. Also updated array syntax to short arrays in several places for consistency.
Copy link
Member

@sapayth sapayth left a comment

Choose a reason for hiding this comment

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

Remove the ACF related or non-related comments. We are not doing anything special for ACF here.
Check older version for implementation of free taxonomy in our form builder.

@sapayth sapayth added needs: author reply This PR needs author feedback or code changes and removed needs: dev review This PR needs review by a developer labels Dec 30, 2025
Revised comments and logic to consistently refer to 'built-in + taxonomies for post/page' instead of 'built-in + ACF taxonomies' when determining free taxonomies. Updated checks and documentation to clarify that only built-in and post/page-associated taxonomies are available in the free version, and custom taxonomies require the pro version.
@arifulhoque7
Copy link
Contributor Author

comment removed @sapayth vai.

here is the previous implementation for custom taxonomies (PRO taxonomies are automatically hidden) :


v4.1.0 Implementation

(No taxonomy filtering – all taxonomies were free)

Admin_Form_Builder.php

// v4.1.0 – Line 169
// ALL form fields are passed directly without any filtering
'form_fields' => wpuf_get_form_fields( $post->ID ),

Form_Field_Post_Taxonomy.php

  • No is_pro() method exists in this class

  • It relies on the default is_pro() implementation from Field_Contract

  • Field_Contract::is_pro() returns false

Behavior in v4.1.0

  • No Pro/Free checks were applied to taxonomy fields

  • ALL taxonomies were rendered, including:

    • Built-in taxonomies (category, post_tag)

    • Custom taxonomies

  • No filtering at:

    • Form Builder level

    • Frontend rendering level

Result

All taxonomies were available in the FREE version in v4.1.0


Current Branch Implementation

(Taxonomy filtering with Free vs Pro separation)

Admin_Form_Builder.php (Lines 172–189)

// Retrieve all original fields
$original_fields = wpuf_get_form_fields( $post->ID );

// Filter out Pro-only taxonomy fields
$filtered_fields = $this->filter_pro_taxonomy_fields( $original_fields );

// Detect whether any taxonomy fields were hidden
$has_hidden_taxonomies = $this->has_filtered_taxonomies(
$original_fields,
$filtered_fields
);

// Collect IDs of hidden taxonomy fields
$hidden_taxonomy_ids = $this->get_hidden_taxonomy_ids(
$original_fields,
$filtered_fields
);

// Pass filtered fields and metadata to JS
'form_fields' => $filtered_fields,
'has_hidden_taxonomies' => $has_hidden_taxonomies,
'hidden_taxonomy_ids' => $hidden_taxonomy_ids,


Taxonomy Classification Logic

Free Taxonomies

Determined via wpuf_get_free_taxonomies():

  • Built-in taxonomies:

    • category

    • post_tag

  • Any taxonomy associated with post or page post types

Pro Taxonomies

  • Any taxonomy not included in the free taxonomy list

  • Typically custom taxonomies not attached to post or page


Filtering & Rendering Behavior (Current Branch)

Admin Form Builder

  • Pro taxonomies are:

    • Removed from the form_fields array

    • Tracked separately for UI notification

  • JS receives:

    • Filtered fields

    • Flags indicating hidden taxonomy fields

    • IDs of hidden taxonomy fields (for notices / upsell UI)

Frontend Rendering

  • Pro taxonomies:

    • Not rendered

  • Enforcement happens in:

    • Render_Form.php

    • Form_Field_Post_Taxonomy.php


Result (Current Branch)

  • FREE users:

    • Can use only free taxonomies

    • Are notified if Pro taxonomy fields exist but are hidden

  • PRO-only taxonomies:

    • Never rendered

    • Fully gated at both builder and frontend levels


Summary Comparison

Aspect v4.1.0 Current Branch
Taxonomy filtering ❌ None ✅ Applied
Pro/Free separation ❌ No ✅ Yes
Admin form builder All fields shown Pro fields removed
Frontend rendering All taxonomies rendered Pro taxonomies blocked
User notification ❌ None ✅ Yes

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
includes/Admin/Forms/Admin_Form_Builder.php (1)

548-578: Inconsistent taxonomy slug extraction with fallback.

Line 571 extracts the slug with a fallback to $f['taxonomy'], but filter_pro_taxonomy_fields (line 529) and get_hidden_taxonomy_ids (line 608) only check $field['name']. This inconsistency was flagged in a previous review. Since all observed field definitions consistently populate the name key, either:

  1. Remove the unnecessary fallback and extract strictly from $f['name'] to align with the other methods, OR
  2. Document why nested fields require the taxonomy key fallback and consider applying it consistently across all three methods.

Based on learnings, the previous review comment recommended removing the fallback unless there's a documented reason for nested fields to use a different key.

🔎 Proposed fix to align slug extraction
             if ( isset( $f['input_type'] ) && $f['input_type'] === 'taxonomy' ) {
-                $slug = $f['name'] ?? ($f['taxonomy'] ?? null);
+                $slug = $f['name'] ?? null;
                 if ( $slug && ! in_array( $slug, $free_taxonomies, true ) ) {
                     return true;
                 }
🧹 Nitpick comments (5)
includes/Render_Form.php (2)

1332-1345: Free-taxonomy gating logic looks correct; consider light optimization

Using wpuf_get_free_taxonomies() here aligns gating with the centralized free-taxonomy definition and should correctly treat ACF/post/page taxonomies as free. If this method runs often in a single request, you might later consider memoizing wpuf_get_free_taxonomies() (static cache in that function) to avoid recomputing the list repeatedly, but it’s not urgent given typical form sizes.


1354-1360: Duplicate gating in taxonomy() vs is_taxonomy_field_gated()

taxonomy() repeats the same free-vs-pro check already encapsulated in is_taxonomy_field_gated(). It’s functionally fine, but for future changes you might reduce drift risk by delegating to a single helper (e.g., if ( $this->is_taxonomy_field_gated( $attr ) ) return;) or extracting a shared wpuf_is_free_taxonomy( $slug ) helper.

wpuf-functions.php (2)

5772-5792: wpuf_get_taxonomy_post_types() is correct but could be slightly simplified

The helper correctly validates the taxonomy and returns $taxonomy->object_type when available, covering both built-in and custom taxonomies. The explicit taxonomy_exists() + null check is a bit redundant since get_taxonomy() already returns false for unknown taxonomies; you could rely on that alone if you want to trim branches, but current code is clear and safe as-is.


5804-5845: Centralized free-taxonomy computation matches intended behavior; consider caching and filter hook

The new wpuf_get_free_taxonomies() correctly:

  • Seeds with ['category', 'post_tag'].
  • Iterates all registered taxonomies.
  • Adds any taxonomy whose object_type includes post or page.

That lines up with the PR intent (built-ins + post/page taxonomies, including ACF ones, as “free”). Two non-blocking suggestions:

  1. Add a static cache inside wpuf_get_free_taxonomies() so multiple calls in a request don’t recompute the same list.
  2. If backward compatibility allows, re-enable the commented wpuf_free_taxonomies filter so site owners can fine-tune what’s treated as free without modifying core plugin code.

Both are incremental maintainability improvements; behavior is already sound.

includes/Admin/Forms/Admin_Form_Builder.php (1)

520-521: Consider caching the free taxonomies result.

wpuf_get_free_taxonomies() is called three times in rapid succession (lines 521, 553, 595) during admin_enqueue_scripts (lines 174-176). While the impact is minor since this occurs only during admin page load, caching the result in a local variable could eliminate redundant taxonomy iterations.

🔎 Proposed optimization

In the admin_enqueue_scripts method around line 173, store the result once:

// Filter pro taxonomy fields and check if any were removed
$original_fields = wpuf_get_form_fields( $post->ID );
$free_taxonomies = wpuf_get_free_taxonomies(); // Cache the result
$filtered_fields = $this->filter_pro_taxonomy_fields( $original_fields, $free_taxonomies );
$has_hidden_taxonomies = $this->has_filtered_taxonomies( $original_fields, $filtered_fields, $free_taxonomies );
$hidden_taxonomy_ids = $this->get_hidden_taxonomy_ids( $original_fields, $filtered_fields, $free_taxonomies );

Then update the three methods to accept an optional $free_taxonomies parameter:

protected function filter_pro_taxonomy_fields( $form_fields, $free_taxonomies = null ) {
    if ( wpuf_is_pro_active() ) {
        return $form_fields;
    }
    
    if ( null === $free_taxonomies ) {
        $free_taxonomies = wpuf_get_free_taxonomies();
    }
    // ... rest of method
}

Apply similar changes to has_filtered_taxonomies and get_hidden_taxonomy_ids.

Also applies to: 552-553, 594-595

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fb0e06 and 220b71e.

📒 Files selected for processing (4)
  • includes/Admin/Forms/Admin_Form_Builder.php
  • includes/Fields/Form_Field_Post_Taxonomy.php
  • includes/Render_Form.php
  • wpuf-functions.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • includes/Fields/Form_Field_Post_Taxonomy.php
🧰 Additional context used
🧬 Code graph analysis (3)
includes/Admin/Forms/Admin_Form_Builder.php (1)
wpuf-functions.php (1)
  • wpuf_get_free_taxonomies (5805-5844)
wpuf-functions.php (1)
includes/Render_Form.php (1)
  • taxonomy (1354-1518)
includes/Render_Form.php (1)
wpuf-functions.php (2)
  • wpuf_get_free_taxonomies (5805-5844)
  • wpuf_is_pro_active (5042-5044)
🔇 Additional comments (4)
wpuf-functions.php (2)

482-487: Short array syntax change is safe

Switching the get_terms() args to short array syntax preserves behavior; taxonomy and arguments are unchanged. No action needed.


3855-3859: Defensive init of $options improves robustness

Normalizing $options to an empty array when get_option( $section ) returns a non-array makes wpuf_update_option() safer against corrupted or legacy option values. This is a good hardening change.

includes/Admin/Forms/Admin_Form_Builder.php (2)

514-539: LGTM!

The dynamic free taxonomy set is correctly integrated. The method now properly filters taxonomy fields against wpuf_get_free_taxonomies() instead of a hardcoded list, enabling ACF and other non-built-in taxonomies to be treated as free when associated with post/page types.


587-617: LGTM!

The method correctly identifies hidden taxonomy field IDs by checking against the dynamic free taxonomy set. The logic properly preserves IDs of filtered-out custom taxonomies for client-side state management.

@arifulhoque7 arifulhoque7 added needs: dev review This PR needs review by a developer and removed needs: author reply This PR needs author feedback or code changes labels Dec 30, 2025
@arifulhoque7 arifulhoque7 requested a review from sapayth December 30, 2025 06:16
@sapayth sapayth added Dev Review Done and removed needs: dev review This PR needs review by a developer labels Jan 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Dev Review Done QA Approved This PR is approved by the QA team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unexpected Behavior in Taxonomy Input Field on Post Form v4.1.8

3 participants