[__CLASS__, 'sanitize_settings']] ); register_setting( 'cooked_settings_group', 'cooked_settings_saved' ); } // Add this new method to handle settings sanitization. public static function sanitize_settings($settings) { $cooked_tabs_fields = self::tabs_fields(); // Process each field foreach ($cooked_tabs_fields as $tab) { if (isset($tab['fields']) && !empty($tab['fields'])) { foreach ($tab['fields'] as $field_name => $field) { // Handle checkbox fields specifically if ($field['type'] === 'checkboxes') { // If the field is missing from submission, set it as an empty array if (!isset($settings[$field_name])) { $settings[$field_name] = []; } else { // Remove any empty string values from checkbox arrays if (!isset($settings[$field_name]) || !is_array($settings[$field_name])) { $settings[$field_name] = []; } else { $settings[$field_name] = array_filter($settings[$field_name], function($value) { return $value !== ''; }); } } } } } } return $settings; } function cooked_settings_saved_admin_notice() { if (isset($_GET['settings-updated']) && $_GET['settings-updated'] && isset($_GET['page']) && $_GET['page'] === 'cooked_settings') { add_settings_error( 'cooked_settings_group', 'cooked_settings_updated', __( 'Cooked settings has been updated!', 'cooked' ), 'updated' ); } } function browse_page_missing_notice() { // Only show on admin pages, not on the Cooked settings page itself if ( isset($_GET['page']) && $_GET['page'] === 'cooked_settings' ) { return; } // Get Cooked settings $_cooked_settings = self::get(); // Check if browse page is set if ( empty($_cooked_settings['browse_page']) || !$_cooked_settings['browse_page'] ) { $class = 'notice notice-warning is-dismissible'; $message = sprintf( '' . __( 'Cooked Plugin Setup', 'cooked' ) . ' ' . __( 'To display your recipes properly, please set up your %s.', 'cooked' ), '' . __( 'Browse/Search Recipes Page', 'cooked' ) . '' ); printf( '

%2$s

', esc_attr( $class ), $message ); } } public static function reset() { global $_cooked_settings; $_cooked_settings = Cooked_Settings::get(); } public static function get() { $_cooked_settings = get_option( 'cooked_settings', [] ); // Get defaults for fields that are not set yet. $cooked_tabs_fields = self::tabs_fields(); if ( isset($cooked_tabs_fields) && !empty($cooked_tabs_fields) ) { foreach ( $cooked_tabs_fields as $tab ) { if ( isset($tab['fields']) && !empty($tab['fields']) ) { foreach ( $tab['fields'] as $name => $field ) { if ( $field['type'] == 'nonce' || $field['type'] == 'misc_button' ) continue; if ( $field['type'] === 'checkboxes' ) { $_cooked_settings[$name] = isset($_cooked_settings[$name]) ? $_cooked_settings[$name] : ( isset( $field['default'] ) ? $field['default'] : [] ); } else { $_cooked_settings[$name] = isset($_cooked_settings[$name]) ? $_cooked_settings[$name] : ( isset( $field['default'] ) ? $field['default'] : false ); } } } } } return apply_filters( 'cooked_get_settings', $_cooked_settings ); } public static function tabs_fields() { $pages_array = self::pages_array( __('Choose a page...','cooked'), __('No pages','cooked') ); $categories_array = self::terms_array( 'cp_recipe_category', __('No default', 'cooked'), __('No categories', 'cooked') ); $recipes_per_page_array = self::per_page_array(); // Dynamically load roles. $role_options = []; if (is_user_logged_in()) { global $wp_roles; $roles = $wp_roles->roles; if (!empty($roles)) { foreach ( $roles as $role => $data ) { $role_options[$role] = [ 'label' => $data['name'] ]; } } } return apply_filters('cooked_settings_tabs_fields', [ 'recipe_settings' => [ 'name' => __('General', 'cooked'), 'icon' => 'gear', 'fields' => [ 'browse_page' => [ 'title' => __('Browse/Search Recipes Page', 'cooked'), /* translators: a description on how to add the [cooked-browse] shortcode to a page */ 'desc' => sprintf(__('Create a page with the %s shortcode on it, then choose it from this dropdown.', 'cooked'), '[cooked-browse]') . '
' . __('Note: This setting is required for the plugin to function properly.', 'cooked') . '', 'type' => 'select', 'default' => 0, 'options' => $pages_array ], 'recipes_per_page' => [ 'title' => __('Recipes Per Page', 'cooked'), /* translators: a description on how to choose the default number of recipes per page. */ 'desc' => sprintf(__('Choose the default (set via the %s panel) or choose a different number here.', 'cooked'), '' . __('Settings > Reading', 'cooked') . ''), 'type' => 'select', 'default' => 9, 'options' => $recipes_per_page_array ], 'recipe_taxonomies' => [ 'title' => __('Recipe Taxonomies', 'cooked'), 'desc' => __('Choose which taxonomies you want to enable for your recipes.', 'cooked'), 'type' => 'checkboxes', 'default' => ['cp_recipe_category'], 'options' => apply_filters( 'cooked_taxonomy_options', [ 'cp_recipe_category' => __('Categories', 'cooked') ] ) ], 'recipe_info_display_options' => [ 'title' => __('Global Recipe Toggles', 'cooked'), 'desc' => __('You can quickly hide or show different recipe elements (site-wide) with these checkboxes.', 'cooked'), 'type' => 'checkboxes', 'default' => apply_filters( 'cooked_recipe_info_display_options_defaults', [ 'author', 'taxonomies', 'difficulty_level', 'excerpt', 'timing_prep', 'timing_cook', 'timing_total', 'servings' ] ), 'options' => apply_filters( 'cooked_recipe_info_display_options', [ 'author' => __('Author', 'cooked'), 'taxonomies' => __('Category', 'cooked'), 'difficulty_level' => __('Difficulty Level', 'cooked'), 'excerpt' => __('Excerpt', 'cooked'), 'notes' => __('Notes', 'cooked'), 'timing_prep' => __('Prep Time', 'cooked'), 'timing_cook' => __('Cook Time', 'cooked'), 'timing_total' => __('Total Time', 'cooked'), 'servings' => __('Servings', 'cooked') ] ) ], 'carb_format' => [ 'title' => __('Carbs Format', 'cooked'), 'desc' => __('You can display carbs as "Total" or "Net".', 'cooked'), 'type' => 'select', 'default' => 'total', 'options' => apply_filters( 'cooked_settings_carb_formats', [ 'total' => __('Total Carbs', 'cooked'), 'net' => __('Net Carbs', 'cooked') ] ) ], 'author_name_format' => [ 'title' => __('Author Name Format', 'cooked'), 'desc' => __('You can show the full author\'s name or just a part of it.', 'cooked'), 'type' => 'select', 'default' => 'full', 'options' => apply_filters( 'cooked_settings_author_formats', [ 'full' => __('Full name', 'cooked'), 'first_last_initial' => __('Full first name w/last name initial', 'cooked'), 'first_initial_last' => __('First name initial w/full last name', 'cooked'), 'first_only' => __('First name only', 'cooked') ] ) ], 'disable_author_links' => [ 'title' => __('Author Links', 'cooked'), 'desc' => __('If you do not want the author names to link to the author recipe listings, you can disable them here.', 'cooked') . '
' . __('Note: Author links require the Browse/Search Recipes Page to be set up correctly to function properly.', 'cooked') . '', 'type' => 'checkboxes', 'color' => 'red', 'default' => [], 'options' => apply_filters( 'cooked_author_link_options', [ 'disabled' => __('Disable Author Links', 'cooked'), ] ) ], 'browse_default_cp_recipe_category' => [ 'title' => __('Default Category', 'cooked'), /* translators: a description on how to set the default recipe category for the [cooked-browse] shortcode. */ 'desc' => sprintf(__('Optionally set the default recipe category for your %s shortcode display.', 'cooked'), '[cooked-browse]'), 'type' => 'select', 'default' => 0, 'options' => $categories_array ], 'browse_default_sort' => [ 'title' => __('Default Sort Order', 'cooked'), /* translators: a description on how to set the default sort order for the [cooked-browse] shortcode. */ 'desc' => sprintf(__('Set the default sort order for your %s shortcode display.', 'cooked'), '[cooked-browse]'), 'type' => 'select', 'default' => 'date_desc', 'options' => apply_filters( 'cooked_settings_sort_options', [ 'date_desc' => __('Newest First', 'cooked'), 'date_asc' => __('Oldest First', 'cooked'), 'title_asc' => __('Alphabetical', 'cooked'), 'title_desc' => __('Alphabetical (reversed)', 'cooked'), ] ) ], 'section_heading_default_html_tag' => [ 'title' => __('Section Heading Default HTML Tag', 'cooked'), /* translators: a description on how to set the default sort order for the [cooked-browse] shortcode. */ 'desc' => __('Set the default HTML tag for your section headings.', 'cooked'), 'type' => 'select', 'default' => 'div', 'options' => apply_filters( 'cooked_settings_section_heading_default_html_tag_options', [ 'div' => __('div', 'cooked'), 'h2' => __('h2', 'cooked'), 'h3' => __('h3', 'cooked'), 'h4' => __('h4', 'cooked'), 'h5' => __('h5', 'cooked'), 'h6' => __('h6', 'cooked'), ] ) ], 'recipe_wp_editor_roles' => [ 'title' => __('WP Editor Roles', 'cooked'), 'desc' => __('Choose which user roles can use the WP Editor for the Excerpt, Directions & Notes fields.', 'cooked'), 'type' => 'checkboxes', 'default' => apply_filters('cooked_recipe_wp_editor_roles_defaults', ['administrator', 'editor', 'cooked_recipe_editor']), 'options' => $role_options ], 'advanced' => [ 'title' => __('Advanced Settings', 'cooked'), 'desc' => '', 'type' => 'checkboxes', 'color' => 'red', 'class' => 'cooked-danger', 'default' => [], 'options' => apply_filters( 'cooked_advanced_options', [ /* translators: an option to only show recipes with the [cooked-recipe] shortcode. */ 'disable_public_recipes' => '' . __('Disable Public Recipes', 'cooked') . ' — ' . sprintf(__('Only show recipes using the %s shortcode.', 'cooked'), '[cooked-recipe]'), /* translators: an option to disable "meta" tags. */ 'disable_meta_tags' => '' . sprintf(__('Disable %s Tags', 'cooked'), 'Cooked <meta>') . ' — ' . __('Prevents duplicates when tags already exist.', 'cooked'), 'disable_servings_switcher' => '' . __('Disable "Servings Switcher"', 'cooked') . ' — ' . __('Removes the servings dropdown on recipes.', 'cooked'), 'disable_schema_output' => '' . __('Disable Recipe Schema Output', 'cooked') . ' — ' . __('You should only do this if you\'re using something else to output schema information.', 'cooked'), 'disable_cp_recipe_archive' => '' . __('Disable Recipe Archive Page', 'cooked') . ' — ' . __('Prevents the recipe archive from being displayed.', 'cooked') ] ) ], ] ], 'design' => [ 'name' => __('Design', 'cooked'), 'icon' => 'pencil', 'fields' => [ 'dark_mode' => [ 'title' => __('Dark Mode', 'cooked'), 'desc' => __('If your site has a dark background, you should enable "Dark Mode" so that Cooked can match this style.', 'cooked'), 'type' => 'checkboxes', 'default' => [], 'options' => apply_filters( 'cooked_dark_mode_options', [ 'enabled' => __('Enable "Dark Mode"', 'cooked'), ] ) ], 'hide_author_avatars' => [ 'title' => __('Author Images', 'cooked'), 'desc' => __('If you do not want to display the author images (avatars), you can disable them here.', 'cooked'), 'type' => 'checkboxes', 'color' => 'red', 'default' => [], 'options' => apply_filters( 'cooked_author_image_options', [ 'hidden' => __('Hide Author Images', 'cooked'), ] ) ], 'main_color' => [ 'title' => __('Main Color', 'cooked'), 'desc' => __('Used on buttons, cooking timer, etc.', 'cooked'), 'type' => 'color_field', 'default' => '#16a780', 'options' => '#16a780' ], 'main_color_hover' => [ 'title' => __('Main Color (on hover)', 'cooked'), 'desc' => __('Used when hovering over buttons.', 'cooked'), 'type' => 'color_field', 'default' => '#1b9371', 'options' => '#1b9371' ], 'responsive_breakpoint_1' => [ 'title' => __('First Responsive Breakpoint', 'cooked'), 'desc' => __('Set the first responsive breakpoint. Best for large tablets.', 'cooked'), 'type' => 'number_field', 'default' => '1000', 'options' => '' ], 'responsive_breakpoint_2' => [ 'title' => __('Second Responsive Breakpoint', 'cooked'), 'desc' => __('Set the second responsive breakpoint. Best for small tablets.', 'cooked'), 'type' => 'number_field', 'default' => '750', 'options' => '' ], 'responsive_breakpoint_3' => [ 'title' => __('Third Responsive Breakpoint', 'cooked'), 'desc' => __('Set the third responsive breakpoint. Best for phones and other small devices.', 'cooked'), 'type' => 'number_field', 'default' => '520', 'options' => '' ] ] ], 'permalinks' => [ 'name' => __('Permalinks', 'cooked'), 'icon' => 'link-lt', 'fields' => [ 'recipe_permalink' => [ 'title' => __('Recipe Permalink', 'cooked'), 'desc' => '', 'type' => 'permalink_field', 'options' => __('recipe-name', 'cooked'), 'default' => 'recipes' ], 'recipe_author_permalink' => [ 'title' => __('Recipe Author Permalink', 'cooked'), 'desc' => '', 'type' => 'permalink_field', 'options' => __('author-name', 'cooked'), 'default' => 'recipe-author' ], 'recipe_category_permalink' => [ 'title' => __('Recipe Category Permalink', 'cooked'), 'desc' => '', 'type' => 'permalink_field', 'options' => __('recipe-category-name', 'cooked'), 'default' => 'recipe-category' ] ] ] ], $pages_array, $categories_array); } public static function per_page_array() { $counter = 0; /* translators: posts_per_page default */ $per_page_array[] = sprintf( __('WordPress Default %s','cooked'), '(' . get_option( 'posts_per_page' ) . ')' ); do { $counter++; $per_page_array[$counter] = $counter; } while ( $counter < 50 ); $per_page_array['-1'] = __('Show All (no pagination)','cooked'); return apply_filters( 'cooked_per_page_options', $per_page_array ); } public static function pages_array( $choose_text, $none_text = false ) { $page_array = []; $pages = get_posts([ 'post_type' => 'page', 'posts_per_page' => -1 ]); if( !empty($pages) ) { $page_array[0] = $choose_text; foreach ($pages as $_page) { $page_array[$_page->ID] = $_page->post_title . ' (ID:' . $_page->ID . ')'; } } elseif ( $none_text ) { $page_array[0] = $none_text; } return apply_filters( 'cooked_settings_pages_array', $page_array ); } public static function terms_array( $term, $choose_text, $none_text = false, $hide_empty = false, $parents_only = false, $child_of = false ) { $terms_array = []; $args = [ 'taxonomy' => $term, 'hide_empty' => $hide_empty ]; if ( $parents_only ) { $args['parent'] = '0'; } elseif ( $child_of ) { $_term = is_numeric($child_of) ? $child_of : get_term_by( 'slug', $child_of, $term ); $term_id = is_object( $_term ) ? $_term->term_id : $_term; $args['parent'] = $term_id; } $terms = get_terms( $args ); if ( !empty($terms) ) { if ($choose_text) { $terms_array[0] = $choose_text; } foreach ($terms as $_term) { if ( !is_array($_term) ) { $terms_array[$_term->term_id] = $_term->name; } } } elseif ( $none_text ) { $terms_array[0] = $none_text; } return apply_filters( 'cooked_settings_' . $term . '_array', $terms_array ); } public static function field_radio( $field_name, $options ) { global $_cooked_settings, $conditions; $counter = 1; echo '

'; foreach ( $options as $value => $name) { $is_disabled = ''; $conditional_value = ''; $conditional_requirement = ''; if ( is_array($name) ): if ( isset($name['read_only']) && $name['read_only'] ): $is_disabled = ' disabled'; endif; if ( isset($name['conditional_value']) && $name['conditional_value'] ): $conditional_value = ' v-model="' . esc_attr($name['conditional_value']) . '"'; if ( !in_array( $name['conditional_value'], $conditions ) ): $conditions[$value] = esc_attr($name['conditional_requirement']); endif; endif; if ( isset($name['conditional_requirement']) && $name['conditional_requirement'] ): if ( is_array($name['conditional_requirement']) ): $conditional_requirement = ' v-show="' . implode( ' && ', $name['conditional_requirement'] ) . '"'; else: $conditional_requirement = ' v-show="' . esc_attr($name['conditional_requirement']) . '"'; endif; endif; $name = $name['label']; endif; $combined_extras = $is_disabled . $conditional_value; if ( $conditional_requirement ): echo ''; endif; echo ''; echo ' '; echo '
'; if ( $conditional_requirement ): echo '
'; endif; $counter++; } echo '

'; } public static function field_select( $field_name, $options, $color = false, $field = []) { global $_cooked_settings; $is_disabled = ''; if ( isset($field['read_only']) && $field['read_only'] ) { $is_disabled = ' disabled'; } echo '

'; echo ''; foreach ( $options as $value => $name) { echo ''; } echo ''; echo '

'; } public static function field_nonce( $field_name, $options ) { wp_nonce_field( $field_name, $field_name ); } // Kept here for backwards compatibility only. Removed used in Cooked Pro 1.0.1 public static function field_misc_button( $field_name, $title ) { echo '

'; echo ''; echo '

'; } // END public static function field_migrate_button( $field_name, $title ) { $old_recipes = get_transient('cooked_classic_recipes'); if ($old_recipes != 'complete') { $total = count($old_recipes); if ($total > 0) { echo '

'; echo ''; echo '

'; echo '

'; echo '0 / ' . esc_html( $total ) . ''; echo '

'; echo '

Migration Complete! You can now ' . __( 'reload', 'cooked' ) . ' the settings screen.

'; } } } public static function field_text($field_name, $placeholder) { global $_cooked_settings; echo '

'; echo ''; echo '

'; } public static function field_password($field_name, $placeholder) { global $_cooked_settings; echo '

'; echo ''; echo '

'; } public static function field_html($field_name, $html) { echo wp_kses_post($html); } public static function field_permalink_field($field_name, $end_of_url) { global $_cooked_settings; $browse_page_id = $_cooked_settings['browse_page']; $browse_page_url = ''; if (!empty($browse_page_id) && $field_name !== 'recipe_permalink') { $browse_page_url = get_permalink( $browse_page_id ); } if (empty($browse_page_url)) { $browse_page_url = get_home_url(); } if (substr($browse_page_url, -1) !== '/') { $browse_page_url .= '/'; } echo ''; } public static function field_number_field( $field_name, $options ) { global $_cooked_settings; echo '

'; echo ''; echo '

'; } public static function field_color_field( $field_name, $default ) { global $_cooked_settings; echo '

'; echo ''; echo '

'; } public static function field_checkboxes($field_name, $options, $color = false, $field = []) { global $_cooked_settings, $conditions; echo '

'; foreach ($options as $value => $name) { $is_disabled = ''; $conditional_value = ''; $conditional_requirement = ''; if (is_array($name)) { if (isset($name['read_only']) && $name['read_only']): $is_disabled = ' disabled'; endif; if (isset($name['conditional_value']) && $name['conditional_value']): $conditional_value = ' v-model="' . esc_attr($name['conditional_value']) . '"'; if ( !in_array( $name['conditional_value'], $conditions ) ): $conditions[$field_name][$name['conditional_value']] = $value; endif; endif; if (isset($name['conditional_requirement']) && $name['conditional_requirement']): if (is_array($name['conditional_requirement'])): $conditional_requirement = ' v-show="' . implode( ' && ', $name['conditional_requirement'] ) . '"'; else: $conditional_requirement = ' v-show="' . esc_attr($name['conditional_requirement']) . '"'; endif; endif; $name = $name['label']; } $combined_extras = $is_disabled . $conditional_value; if ($conditional_requirement) { echo ''; } // Check if the setting exists at all $setting_exists = isset($_cooked_settings[$field_name]) && is_array($_cooked_settings[$field_name]); // Only use default if setting doesn't exist $use_default = !$setting_exists && isset($field['default']); // Determine if checkbox should be checked $is_checked = ($setting_exists && in_array($value, $_cooked_settings[$field_name])) || ($use_default && in_array($value, (array)$field['default'])) || $is_disabled; if ($is_disabled) { echo ''; echo ''; } else { echo ''; } echo ' '; echo '
'; if ($conditional_requirement) { echo '
'; } } echo '

'; } }