'case-studies', 'category_base' => 'category', 'url_mode' => 'base', // base = /case-studies/slug/, root = /slug/ // Listing page 'archive_title' => 'Case Studies', 'archive_intro' => '', 'per_page' => 9, 'columns' => 3, 'archive_layout' => 'grid', // grid | list 'show_filters' => 1, // Single case study 'toc_enabled' => 1, 'toc_title' => 'On this page', 'toc_depth' => 'h2h3', 'toc_min' => 3, 'related_count' => 3, 'related_layout' => 'grid', // grid | list 'show_author' => 1, 'author_label' => 'Written by', // Default call to action 'cta_heading' => 'Want results like these?', 'cta_text' => 'Tell us about your project and we will get back to you within one business day.', 'cta_button' => 'Get in touch', 'cta_url' => '', 'cta_new_tab' => 0, 'cta_nofollow' => 0, // Design 'design_preset' => 'modern', 'site_tone' => 'auto', // auto | light | dark - the theme's own page background 'color_primary' => '', 'color_accent' => '', 'color_heading' => '', 'color_text' => '', 'color_bg' => '', 'color_card' => '', 'font_heading' => 'inherit', 'font_body' => 'inherit', 'base_size' => 17, // paragraph size on desktop 'p_size_m' => 16, // paragraph size on phones 'h1_size' => 37, 'h1_size_m' => 28, 'h2_size' => 30, 'h2_size_m' => 24, 'h3_size' => 21, 'h3_size_m' => 19, 'use_gradients' => 1, 'icon_colors' => 'brand', // brand (theme colour) | mixed (colourful) 'decorations' => 1, 'radius' => 14, 'content_width' => 1140, 'card_style' => 'shadow', // shadow | border | flat ]; } /** * Returns all settings (or one key), merged with defaults. */ function sahcs_get_settings($key = null) { static $cache = null; if ($cache === null) { $cache = sahcs_fresh_settings(); } // Translated texts (Polylang) are looked up each time, because the // page language is only known after the settings are first read. $settings = function_exists('sahcs_translate_settings') ? sahcs_translate_settings($cache) : $cache; if ($key === null) { return $settings; } return isset($settings[$key]) ? $settings[$key] : null; } /** Same as sahcs_get_settings() but always re-reads from the database. */ function sahcs_fresh_settings() { $saved = get_option('sahcs_settings', []); return array_merge(sahcs_default_settings(), is_array($saved) ? $saved : []); } function sahcs_sanitize_hex($value) { $value = sahcs_trim((string) $value); if ($value === '') { return ''; } $hex = sanitize_hex_color($value); return $hex ? $hex : ''; } function sahcs_sanitize_slug($value, $fallback) { $parts = array_filter(array_map('sanitize_title', explode('/', (string) $value))); $slug = implode('/', $parts); return $slug !== '' ? $slug : $fallback; } /** * Sanitises the whole settings array coming from the settings form. * Only the keys present in the submitted tab are changed; the rest keep * their saved values. */ function sahcs_sanitize_settings($input) { $current = sahcs_fresh_settings(); $defaults = sahcs_default_settings(); $input = is_array($input) ? $input : []; $out = $current; $tab = isset($input['_tab']) ? sanitize_key($input['_tab']) : ''; // WordPress may run this callback a second time on the already-cleaned // array (no "_tab" key). Then every value is checked again instead of // falling back to the old saved settings. if ($tab !== '') { $tabs = [$tab]; } elseif (isset($input['archive_slug'], $input['design_preset'])) { $tabs = ['general', 'design']; } else { $tabs = []; } if (in_array('general', $tabs, true)) { $out['archive_slug'] = sahcs_sanitize_slug(isset($input['archive_slug']) ? $input['archive_slug'] : '', $defaults['archive_slug']); $out['category_base'] = sahcs_sanitize_slug(isset($input['category_base']) ? $input['category_base'] : '', $defaults['category_base']); $out['url_mode'] = (isset($input['url_mode']) && $input['url_mode'] === 'root') ? 'root' : 'base'; $out['archive_title'] = sanitize_text_field(isset($input['archive_title']) ? $input['archive_title'] : ''); $out['archive_intro'] = sanitize_textarea_field(isset($input['archive_intro']) ? $input['archive_intro'] : ''); $out['per_page'] = max(1, min(60, absint(isset($input['per_page']) ? $input['per_page'] : 9))); $out['columns'] = max(1, min(4, absint(isset($input['columns']) ? $input['columns'] : 3))); $out['archive_layout'] = (isset($input['archive_layout']) && $input['archive_layout'] === 'list') ? 'list' : 'grid'; $out['show_filters'] = empty($input['show_filters']) ? 0 : 1; $out['toc_enabled'] = empty($input['toc_enabled']) ? 0 : 1; $out['toc_title'] = sanitize_text_field(isset($input['toc_title']) ? $input['toc_title'] : ''); $out['toc_depth'] = (isset($input['toc_depth']) && $input['toc_depth'] === 'h2') ? 'h2' : 'h2h3'; $out['toc_min'] = max(1, min(10, absint(isset($input['toc_min']) ? $input['toc_min'] : 3))); $out['related_count'] = max(0, min(12, absint(isset($input['related_count']) ? $input['related_count'] : 3))); $out['related_layout'] = (isset($input['related_layout']) && $input['related_layout'] === 'list') ? 'list' : 'grid'; $out['show_author'] = empty($input['show_author']) ? 0 : 1; $out['author_label'] = sanitize_text_field(isset($input['author_label']) ? $input['author_label'] : ''); $out['cta_heading'] = sanitize_text_field(isset($input['cta_heading']) ? $input['cta_heading'] : ''); $out['cta_text'] = sanitize_textarea_field(isset($input['cta_text']) ? $input['cta_text'] : ''); $out['cta_button'] = sanitize_text_field(isset($input['cta_button']) ? $input['cta_button'] : ''); $out['cta_url'] = esc_url_raw(isset($input['cta_url']) ? $input['cta_url'] : ''); $out['cta_new_tab'] = empty($input['cta_new_tab']) ? 0 : 1; $out['cta_nofollow'] = empty($input['cta_nofollow']) ? 0 : 1; // The two slugs must differ or category pages would swallow case studies. if ($out['category_base'] === $out['archive_slug']) { $out['category_base'] = $defaults['category_base']; } } if (in_array('design', $tabs, true)) { $presets = array_keys(sahcs_design_presets()); $out['design_preset'] = (isset($input['design_preset']) && in_array($input['design_preset'], $presets, true)) ? $input['design_preset'] : 'modern'; $out['site_tone'] = (isset($input['site_tone']) && in_array($input['site_tone'], ['auto', 'light', 'dark'], true)) ? $input['site_tone'] : 'auto'; foreach (['color_primary', 'color_accent', 'color_heading', 'color_text', 'color_bg', 'color_card'] as $k) { $out[$k] = sahcs_sanitize_hex(isset($input[$k]) ? $input[$k] : ''); } $fonts = array_keys(sahcs_font_choices()); foreach (['font_heading', 'font_body'] as $k) { $val = isset($input[$k]) ? sanitize_text_field(wp_unslash($input[$k])) : 'inherit'; $out[$k] = in_array($val, $fonts, true) ? $val : 'inherit'; } $out['base_size'] = max(14, min(22, absint(isset($input['base_size']) ? $input['base_size'] : 17))); $sizes = ['p_size_m' => [13, 22, 16], 'h1_size' => [24, 72, 37], 'h1_size_m' => [20, 56, 28], 'h2_size' => [18, 56, 30], 'h2_size_m' => [16, 44, 24], 'h3_size' => [14, 40, 21], 'h3_size_m' => [13, 32, 19]]; foreach ($sizes as $k => $r) { $out[$k] = isset($input[$k]) && $input[$k] !== '' ? max($r[0], min($r[1], absint($input[$k]))) : $r[2]; } $out['use_gradients'] = empty($input['use_gradients']) ? 0 : 1; $out['icon_colors'] = (isset($input['icon_colors']) && $input['icon_colors'] === 'mixed') ? 'mixed' : 'brand'; $out['decorations'] = empty($input['decorations']) ? 0 : 1; $out['radius'] = max(0, min(32, absint(isset($input['radius']) ? $input['radius'] : 14))); $out['content_width'] = max(720, min(1600, absint(isset($input['content_width']) ? $input['content_width'] : 1140))); $out['card_style'] = (isset($input['card_style']) && in_array($input['card_style'], ['shadow', 'border', 'flat'], true)) ? $input['card_style'] : 'shadow'; } unset($out['_tab']); return $out; } /** Flush permalinks on the next request after URL settings change. */ add_action('update_option_sahcs_settings', function ($old, $new) { $keys = ['archive_slug', 'category_base', 'url_mode']; foreach ($keys as $k) { if (!isset($old[$k]) || !isset($new[$k]) || $old[$k] !== $new[$k]) { update_option('sahcs_flush_rewrite', 1, false); break; } } }, 10, 2); add_action('init', function () { if (get_option('sahcs_flush_rewrite')) { delete_option('sahcs_flush_rewrite'); flush_rewrite_rules(false); } }, 99);