# Feature Request: Enable Custom Conditions for Theme Templates

## Summary

Custom conditions for theme templates are partially implemented but non-functional. The UI exists and allows users to select "Custom" mode, but:
1. Custom conditions are not saved (skipped during save if type is empty)
2. Custom condition evaluation is commented out in the backend

## User Story / Problem

A user wants to display different templates for specific taxonomy terms within the same taxonomy:

> "For EDD we have a docs page with categories. All 'Categories' DO NOT have any sub categories, except for one 'Extensions & Themes'. I want to display a different template for it. I can't figure out the Conditions to make the priority 10 template to load only for that one category."

**Current limitation:** The `is_tax(x)` condition matches ALL terms in a taxonomy, but users can't target a **specific term** without custom conditions working.

## Technical Analysis

### 1. UI Implementation (Working)

The admin UI correctly shows Custom as a condition mode option:

**File:** `admin/js/admin.js` (lines 4098-4101)
```javascript
var conditionModes = seedprod_admin.condition_modes || {
    'include': 'Include',
    'exclude': 'Exclude',
    'custom': 'Custom'
};
```

### 2. Save Function Bug (Broken)

Custom conditions don't save because the save function requires a non-empty type:

**File:** `admin/includes/theme-functions.php` (lines 335-343)
```php
foreach ( $conditions as $condition ) {
    if ( isset( $condition['type'] ) && ! empty( $condition['type'] ) ) {  // <-- BUG: Custom mode has empty type
        $sanitized_condition    = array(
            'type'      => sanitize_text_field( $condition['type'] ),
            'condition' => isset( $condition['condition'] ) ? sanitize_text_field( $condition['condition'] ) : 'include',
            'value'     => isset( $condition['value'] ) ? sanitize_text_field( $condition['value'] ) : '',
        );
        $sanitized_conditions[] = $sanitized_condition;
    }
}
```

**Fix needed:** Allow saving when `condition === 'custom'` regardless of type being empty.

### 3. Evaluation Code (Commented Out)

The custom condition evaluation is disabled:

**File:** `app/render-theme-template.php` (lines 595-619)
```php
// check custom condition.
// if ( ! empty( $conditions->custom ) ) {
// #TODO - whitelist functions that can be passed in.
// try {
// foreach ( $conditions->custom as $k3 => $v3 ) {
// $cond_func = $v3['value'];
// ...
// if ( @call_user_func( $cond_func, $values ) ) {
//     $conditions_meet = true;
// }
// ...
// } catch ( Exception $e ) {
// }
// }
```

**Note:** The TODO comment mentions needing a whitelist of allowed functions for security.

### 4. Condition Routing (Working)

Custom conditions are correctly routed to a separate array:

**File:** `app/render-theme-template.php` (lines 501-506)
```php
if ( 'custom' === $v0->condition ) {
    $conditions->custom[] = array(
        'type'  => $v0->type,
        'value' => $v0->value,
    );
}
```

## Security Considerations

The main concern (noted in TODO) is that custom conditions allow arbitrary function execution. This requires:

1. **Function Whitelist:** Only allow known-safe WordPress conditional functions
2. **Prefix Validation:** Ensure only `is_*`, `has_*`, or `seedprod_*` functions are allowed
3. **Argument Sanitization:** Validate and sanitize all function arguments

### Recommended Whitelist

```php
$allowed_custom_functions = array(
    // WordPress core conditionals
    'is_tax',
    'is_category',
    'is_tag',
    'is_page',
    'is_single',
    'is_singular',
    'is_archive',
    'is_post_type_archive',
    'is_author',
    'is_date',
    'is_year',
    'is_month',
    'is_day',
    'is_search',
    'is_404',
    'is_front_page',
    'is_home',
    'is_attachment',
    'has_category',
    'has_tag',
    'has_term',
    'in_category',

    // WooCommerce (if active)
    'is_shop',
    'is_product',
    'is_product_category',
    'is_product_tag',
    'is_cart',
    'is_checkout',
    'is_account_page',

    // EDD (if active)
    'edd_is_checkout',

    // SeedProd custom
    'seedprod_pro_is_child_of',
);
```

## Implementation Plan

### Phase 1: Fix Save Bug (Quick Win)

Update save function to handle custom conditions:

```php
foreach ( $conditions as $condition ) {
    $is_custom = isset( $condition['condition'] ) && 'custom' === $condition['condition'];

    // Allow saving if type is set OR if it's a custom condition with a value
    if ( ( isset( $condition['type'] ) && ! empty( $condition['type'] ) ) ||
         ( $is_custom && ! empty( $condition['value'] ) ) ) {

        $sanitized_condition = array(
            'type'      => sanitize_text_field( $condition['type'] ?? '' ),
            'condition' => sanitize_text_field( $condition['condition'] ?? 'include' ),
            'value'     => sanitize_text_field( $condition['value'] ?? '' ),
        );
        $sanitized_conditions[] = $sanitized_condition;
    }
}
```

### Phase 2: Enable Custom Condition Evaluation

Uncomment and secure the evaluation code:

```php
// Check custom condition.
if ( ! empty( $conditions->custom ) ) {
    // Whitelist of allowed functions.
    $allowed_functions = apply_filters( 'seedprod_custom_condition_functions', array(
        'is_tax', 'is_category', 'is_tag', 'is_page', 'is_single',
        'is_singular', 'is_archive', 'is_post_type_archive', 'is_author',
        'is_date', 'is_search', 'is_404', 'is_front_page', 'is_home',
        'has_category', 'has_tag', 'has_term', 'in_category',
        'seedprod_pro_is_child_of',
    ));

    // Add WooCommerce functions if active.
    if ( class_exists( 'WooCommerce' ) ) {
        $allowed_functions = array_merge( $allowed_functions, array(
            'is_shop', 'is_product', 'is_product_category',
            'is_product_tag', 'is_cart', 'is_checkout',
        ));
    }

    foreach ( $conditions->custom as $v3 ) {
        $cond_func = $v3['value'];
        $values    = '';

        // Extract function and arguments.
        if ( strpos( $cond_func, '(' ) !== false ) {
            preg_match( '#^([^\(]+)\(([^\)]*)\)$#', $cond_func, $match );
            if ( ! empty( $match ) ) {
                $cond_func = $match[1];
                $values    = $match[2];
            }
        }

        // Security: Only allow whitelisted functions.
        if ( ! in_array( $cond_func, $allowed_functions, true ) ) {
            continue;
        }

        // Sanitize values.
        $values = array_map( 'sanitize_text_field', explode( ',', $values ) );
        if ( empty( $values[0] ) ) {
            $values = '';
        }

        // Evaluate condition.
        if ( function_exists( $cond_func ) && @call_user_func( $cond_func, $values ) ) {
            $conditions_meet = true;
            break;
        }
    }
}
```

### Phase 3: UI Improvements (Optional)

Consider adding:
1. Autocomplete/dropdown for common custom functions
2. Help text explaining custom condition syntax
3. Validation feedback when entering custom conditions

## Alternative Solution: Add Specific Term Targeting

Instead of (or in addition to) enabling full custom conditions, add specific term targeting to existing condition types:

**Current:** `is_tax(x)` - matches any term in taxonomy
**Proposed:** Add a second field for term slug/ID

This would solve the user's immediate use case without the security complexity of arbitrary functions.

## Files to Modify

1. `admin/includes/theme-functions.php` - Fix save function
2. `app/render-theme-template.php` - Enable evaluation with security
3. `admin/js/admin.js` - Update UI validation (optional)
4. `app/functions-utils.php` - Add whitelist filter/function

## Priority Note for User's Immediate Workaround

Until this is implemented, the user can use a **PHP filter**:

```php
add_filter( 'seedprod_theme_template', function( $template_id, $template_type ) {
    // Only modify archive templates
    if ( 'archive' !== $template_type ) {
        return $template_id;
    }

    // Check if we're on the specific EDD docs category
    if ( is_tax( 'edd_doc_category', 'extensions-themes' ) ) {
        // Return the ID of the "Extensions & Themes" template
        return YOUR_TEMPLATE_ID_HERE;
    }

    return $template_id;
}, 10, 2 );
```

## Labels

- `enhancement`
- `theme-builder`
- `conditions`

## Related

- Original TODO comment in `render-theme-template.php:597`
- User support request about EDD docs categories
