# Skill: Settings Management

## Identity
- **Skill ID**: `settings-management`
- **Domain**: Plugin Settings & Configuration
- **Technologies**: WordPress Settings API, Options API
- **Source Agent**: `settings-expert.md`

## When to Load This Skill
- Task involves plugin settings pages
- API key validation and storage
- Configuration management
- Moderation settings (banned words, throttling)
- Files matching: `admin/views/settings*.php`, `includes/**/Settings.php`

## Core Patterns

### Settings Validation & Sanitization
```php
function presszone_international_handle_settings() {
    // Verify nonce
    if (!wp_verify_nonce($_POST['nonce'], 'presszone_international_settings')) {
        wp_die(__('Security check failed', 'international-press-zone'));
    }

    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_die(__('Insufficient permissions', 'international-press-zone'));
    }

    $settings = [];

    // Validate language code against whitelist
    if (isset($_POST['default_language'])) {
        $lang = sanitize_key($_POST['default_language']);
        $allowed = presszone_international_get_supported_languages();
        if (array_key_exists($lang, $allowed)) {
            $settings['default_language'] = $lang;
        }
    }

    // Boolean settings
    $settings['auto_detect'] = isset($_POST['auto_detect']) ? 1 : 0;
    $settings['show_flags'] = isset($_POST['show_flags']) ? 1 : 0;

    // Save settings
    foreach ($settings as $key => $value) {
        update_option("presszone_international_{$key}", $value);
    }

    add_settings_error('presszone_international_settings', 'saved',
        __('Settings saved successfully', 'international-press-zone'), 'updated');
}
```

### API Key Secure Storage
```php
function presszone_international_save_api_key($api_key) {
    $api_key = sanitize_text_field($api_key);

    // Validate format
    if (!preg_match('/^[a-zA-Z0-9_-]{32,128}$/', $api_key)) {
        return new WP_Error('invalid_format', __('Invalid API key format', 'international-press-zone'));
    }

    // Store securely (no autoload for sensitive data)
    $encrypted = presszone_international_encrypt_data($api_key);
    update_option('presszone_international_api_key', $encrypted, false);

    return true;
}

function presszone_international_encrypt_data($data) {
    if (!function_exists('openssl_encrypt')) {
        return base64_encode($data);
    }

    $key = wp_salt('AUTH_KEY');
    $iv = openssl_random_pseudo_bytes(16);
    $encrypted = openssl_encrypt($data, 'AES-256-CBC', $key, 0, $iv);

    return base64_encode($iv . $encrypted);
}

function presszone_international_decrypt_data($encrypted) {
    if (!function_exists('openssl_decrypt')) {
        return base64_decode($encrypted);
    }

    $data = base64_decode($encrypted);
    $key = wp_salt('AUTH_KEY');
    $iv = substr($data, 0, 16);
    $encrypted = substr($data, 16);

    return openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, $iv);
}
```

### Accessible Settings Form
```php
function presszone_international_render_settings_form() {
    $settings = presszone_international_get_settings();
    ?>
    <div class="wrap">
        <h1><?php esc_html_e('International Settings', 'international-press-zone'); ?></h1>

        <?php settings_errors('presszone_international_settings'); ?>

        <form method="post" action="" novalidate>
            <?php wp_nonce_field('presszone_international_settings', 'nonce'); ?>

            <table class="form-table" role="presentation">
                <tr>
                    <th scope="row">
                        <label for="default-language">
                            <?php esc_html_e('Default Language', 'international-press-zone'); ?>
                        </label>
                    </th>
                    <td>
                        <select id="default-language"
                                name="default_language"
                                aria-describedby="default-language-desc">
                            <?php foreach (presszone_international_get_languages() as $code => $name): ?>
                                <option value="<?php echo esc_attr($code); ?>"
                                        <?php selected($settings['default_language'], $code); ?>>
                                    <?php echo esc_html($name); ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                        <p id="default-language-desc" class="description">
                            <?php esc_html_e('The primary language for your site.', 'international-press-zone'); ?>
                        </p>
                    </td>
                </tr>

                <tr>
                    <th scope="row">
                        <?php esc_html_e('Display Options', 'international-press-zone'); ?>
                    </th>
                    <td>
                        <fieldset>
                            <legend class="screen-reader-text">
                                <?php esc_html_e('Display Options', 'international-press-zone'); ?>
                            </legend>
                            <label>
                                <input type="checkbox"
                                       name="show_flags"
                                       value="1"
                                       <?php checked($settings['show_flags'], 1); ?>>
                                <?php esc_html_e('Show country flags', 'international-press-zone'); ?>
                            </label>
                            <br>
                            <label>
                                <input type="checkbox"
                                       name="auto_detect"
                                       value="1"
                                       <?php checked($settings['auto_detect'], 1); ?>>
                                <?php esc_html_e('Auto-detect visitor language', 'international-press-zone'); ?>
                            </label>
                        </fieldset>
                    </td>
                </tr>
            </table>

            <?php submit_button(__('Save Settings', 'international-press-zone')); ?>
        </form>
    </div>
    <?php
}
```

### Banned Words Filter (with Wildcards)
```php
function presszone_international_check_banned_words($content, $banned_words) {
    $content_lower = mb_strtolower($content);

    foreach ($banned_words as $word) {
        $word = sanitize_text_field($word);

        if (strpos($word, '*') !== false) {
            // Wildcard pattern: *word* matches anywhere
            $pattern = str_replace('*', '.*', preg_quote($word, '/'));
            if (preg_match("/{$pattern}/iu", $content_lower)) {
                return true;
            }
        } else {
            // Exact word match with word boundaries
            $pattern = '\b' . preg_quote($word, '/') . '\b';
            if (preg_match("/{$pattern}/iu", $content_lower)) {
                return true;
            }
        }
    }

    return false;
}
```

## Anti-Patterns (Forbidden)

| Mistake | Fix |
|---------|-----|
| Storing API keys with autoload | Use `update_option($key, $value, false)` |
| Missing capability check | Always use `current_user_can('manage_options')` |
| Insecure regex patterns | Use `preg_quote()` for user patterns |
| Missing nonce verification | Verify nonce before processing |
| Logging sensitive data | Never log API keys or passwords |
| Hardcoded option names | Use `presszone_international_*` prefix |
| No input sanitization | Sanitize ALL settings input |

## WordPress.org Compliance

### Option Naming
- All options: `presszone_international_*`
- Sensitive options: Set autoload to `false`
- Group related options in arrays when possible

### Settings Registration
```php
add_action('admin_init', function() {
    register_setting(
        'presszone_international_settings',
        'presszone_international_default_language',
        [
            'type' => 'string',
            'sanitize_callback' => 'sanitize_key',
            'default' => 'en'
        ]
    );
});
```

## Integration with Other Skills
- **Often combined with**: `wordpress-php-integration`, `admin-panel-fullstack`
- **For database storage**: Load `database-operations`
- **For API validation**: Load `api-integration`

## Quick Reference

### Get/Set Settings Pattern
```php
function presszone_international_get_settings() {
    return [
        'default_language' => get_option('presszone_international_default_language', 'en'),
        'show_flags' => get_option('presszone_international_show_flags', 1),
        'auto_detect' => get_option('presszone_international_auto_detect', 0),
    ];
}
```

### Settings Sections
```php
add_settings_section(
    'presszone_international_general',
    __('General Settings', 'international-press-zone'),
    'presszone_international_section_callback',
    'presszone_international_settings'
);
```

## Validation Checklist
- [ ] Nonce verified before processing
- [ ] Capability check (`manage_options`)
- [ ] All inputs sanitized
- [ ] Sensitive data encrypted
- [ ] Autoload disabled for sensitive options
- [ ] Form fields have proper labels
- [ ] Help text associated with `aria-describedby`
- [ ] Error messages accessible
- [ ] Options use `presszone_international_` prefix
