使用面向对象编程创建主题​​选项

淮城一只猫 · · 452次浏览 ·

前言

由于新开插件坑,需要选项支持,故写了这篇文章,还是以官网默认主题为主(twentysixteen)。
由于篇幅过长的问题,细节上的不再描述,直接贴上代码分享吧。

教程

在主题目录下新建settings文件夹目录,分别新建settings.css settings.js settings.php文件:

settings.css

/*
1. Theme Settings General
1.1 Checkbox
1.2 Input Field
1.3 Image Field
*/
/*************** 1. Theme Settings General ***************/
.theme_settings_inner_content {
    padding: 30px;
    background: #fff;
    margin-top: 20px;
}
.settings_above_meta .save_all {
    text-decoration: none;
    background: #ddd;
    border-radius: 3px;
    margin-top: 15px;
    text-transform: uppercase;
    padding: 8px 10px;
    display: inline-block;
    color: #4e4a4a;
    -webkit-transition: all 200ms ease-in;
    transition: all 200ms ease-in;
}
.settings_above_meta .save_all:hover {
    background: #ccc;
}
.settings_above_meta .save_all:active,
.settings_above_meta .save_all:focus {
    border: 0;
    box-shadow: none;
    outline: 0;
}
.settings_above_meta .spinner {
    background: url(spinner.gif) no-repeat;
    -webkit-background-size: 20px 20px;
    background-size: 20px 20px;
    display: inline-block;
    visibility: hidden;
    float:none;
    vertical-align: middle;
    opacity: .7;
    filter: alpha(opacity=70);
    width: 20px;
    height: 20px;
    margin: -2px 10px 0;
}
.settings_above_meta .saved_options {
    color: #3eb72f;
    font-weight: 600;
    text-transform: uppercase;
}
.twentysixteen_options_left {
    width: 30%;
    margin-left: 30px;
    display: inline-block;
    vertical-align: top;
    margin-bottom: 20px;
}
.twentysixteen_options_right {
    width: 60%;
    display: inline-block;
    vertical-align: top;
    margin-bottom: 20px;
}
/*****---------- 1.1 Checkbox ----------*****/
input[type="checkbox"].twentysixteen_setting {
    display: none;
}
.checkbox_label{
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
.twentysixteen_setting.checkbox {
    width: 25px;
    height: 25px;
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 3px;
    margin-right: 20px;
    display: inline-block;
    vertical-align: middle;
    position: relative;
}
.twentysixteen_setting.checkbox:before {
    content: '✔';
    color: transparent;
    position: absolute;
    top: 5px;
    left: 6px;
    font-size: 19px;
    line-height: 0.8;
    -webkit-transition: all 150ms ease-in;
    transition: all 150ms ease-in;
}
.twentysixteen_setting.checkbox.checked:before {
    content: '✔';
    color: #2bb1ef;
}
/*****---------- 1.2 Input Field ----------*****/
.twentysixteen_options_wrapper input[type="text"] {
    background: #fff;
    border: 1px solid #ddd;
    padding: 3px 10px;
    border-radius: 3px;
    box-shadow: none;
}
.twentysixteen_options_wrapper input[type="text"]:active,
.twentysixteen_options_wrapper input[type="text"]:focus {
    border: 1px solid #ddd;
    box-shadow: none;
    outline: 0;
}
/*****---------- 1.3 Image Field ----------*****/
.twentysixteen_uploaded_image img {
    max-width: 30%;
    margin-bottom: 20px;
}

settings.js

jQuery( document ).ready(function( $) {
    "use strict";

    $( document )
        .on('click', '#twentysixteen_settings_save', settings_save )
        .on('click', '.checkbox_label', checkbox_switch );


    /********* Checkbox Switch ***********/

    function checkbox_switch(e) {
        e.preventDefault();
        var $this = $(this);
        var utter_hide_layout = $this.data('settings');
        var $hidden_input = $this.prev('input');
        var $checkbox = $this.find('.checkbox');

        if ( $checkbox.hasClass('checked') ) {
            $checkbox.removeClass('checked');
            $hidden_input.val('0');
            $( utter_hide_layout ).hide();
        } else {
            $checkbox.addClass('checked');
            $hidden_input.val('1');
            $( utter_hide_layout ).show();
        }
    }

    function settings_save(e) {
        e.preventDefault();
        var $saved = $('.settings_above_meta .saved_options');
        var $spinner = $('.settings_above_meta .spinner');
        var settings_out = {};
        settings_out['settings'] = {};
        settings_out['settings']['google_api_key'] = $('.twentysixteen_options_wrapper .google_api_key').val();
        settings_out['settings']['google_maps_enable'] = $('.twentysixteen_options_wrapper .google_maps_enable').val();
        $.ajax({
            type: 'POST',
            url: ajaxurl,
            data: {
                'action' : 'twentysixteen_settings_save',
                'data' : JSON.stringify( settings_out, null, 2 ),
                'twentysixteen_settings_nonce' : $('#twentysixteen_settings_nonce').val()
            },
            success: function(data) {
                $saved.text( twentysixteen_settings_object.saved ).delay( 2000 ).fadeOut();
            },
            beforeSend : function () {
                $saved.text('').show();
                $spinner.css('visibility', 'visible');

            },
            error : function (jqXHR, textStatus, errorThrown) {
                $loader.html( jqXHR + ' :: ' + textStatus + ' :: ' + errorThrown );
            },
            complete : function () {
                $spinner.css('visibility', 'hidden');
            }
        });
    }

});

settings.php

<?php
/**
 * Theme Settings
 *
 * @package    WordPress
 * @subpackage twentysixteen/inc/theme-settings
 * @version    1.0.0
 * @author     Made by Denis <https://madebydenis.com/>
 * @license    https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3
 * @link       https://twentysixteen.io
 * @since      1.0.0
 */
add_filter('admin_init', 'twentysixteen_theme_settings_capability');
/**
 * Restrict access to the theme settings page to admins
 *
 * @since  1.0.0
 * @return void If user is not administrator they won't see Theme Settings
 */
function twentysixteen_theme_settings_capability() {
    if (!current_user_can('manage_options')) {
        return;
    }
}

add_action('admin_menu', 'twentysixteen_theme_settings_add_page');
/**
 * Theme Settings add menu page
 *
 * @since  1.0.0
 * @return void The resulting page's hook_suffix
 */
function twentysixteen_theme_settings_add_page() {
    add_menu_page(esc_html__('Theme Settings', 'twentysixteen'), esc_html__('Theme Settings', 'twentysixteen'), 'edit_theme_options', 'twentysixteen_theme_settings', 'twentysixteen_settings_render_page');
}

/**
 * Theme Settings page builder function
 *
 * @param  string $title The title of the page.
 * @param  array $values Array of the setting values from the database.
 *
 * @return string         Returned compiled HTML of the page
 */
function twentysixteen_build_settings_html($title, $values = null) {
    $out = '';
    $value = 0;
    $checked = '';
    $maps_enabled = (isset($values) && '' !== $values) ? $values->google_maps_enable : '';
    if (isset($maps_enabled) && '1' === $maps_enabled) {
        $value = 1;
        $checked = 'checked';
    }
    $out .= '<h3>' . esc_html__('Settings', 'twentysixteen') . '</h3>
<div class="twentysixteen_options_wrapper">
<h4>' . esc_html__('Enable Google Maps', 'twentysixteen') . '</h4>
<input type="hidden" class="twentysixteen_setting google_maps_enable" name="google_maps_enable" value="' . $value . '">
<label class="checkbox_label" for="google_maps_enable" data-settings=".twentysixteen_google_maps_api_key"><span class="twentysixteen_setting checkbox ' . $checked . '"></span>' . esc_html__('Enable google map', 'twentysixteen') . '</label>
<p class="info">' . esc_html__('Use this if you are not using external plugin to power the Google Maps . ', 'twentysixteen') . '</p>
</div>';
    if (isset($maps_enabled) && '1' === $maps_enabled) {
        $out .= '<div class="twentysixteen_options_wrapper twentysixteen_google_maps_api_key show">
<h4>' . esc_html__('Add Google Maps API key', 'twentysixteen') . '</h4>
<input type="text" class="twentysixteen_setting google_api_key" name="google_api_key" value="' . esc_attr($values->google_api_key) . '" />
<p class="info">' . esc_html__('You need to find your API key? Just click on this link: ', 'twentysixteen') . '<a href="https://console.developers.google.com/">' . esc_html__('Google API keys', 'twentysixteen') . '</a>.</p>
</div>';
    } else {
        $out .= '<div class="twentysixteen_options_wrapper twentysixteen_google_maps_api_key" style="display:none;">
<h4>' . esc_html__('Add Google Maps API key', 'twentysixteen') . '</h4>
<input type="text" class="twentysixteen_setting google_api_key" name="google_api_key" value="" />
<p class="info">' . esc_html__('You need to find your API key? Just click on this link: ', 'twentysixteen') . '<a href="https://console.developers.google.com/">' . esc_html__('Google API keys', 'twentysixteen') . '</a>.</p>
</div>';
    }
    return $out;
}

/**
 * Theme Settings page render function
 *
 * @return void
 */
function twentysixteen_settings_render_page() {
    ?>
    <div class="wrap">
    <h2 class="page_title"><?php esc_html_e('Theme Settings', 'twentysixteen') ?></h2>
    <p class="description"><?php esc_html_e('General theme settings', 'twentysixteen') ?></p>
    <div class="settings_above_meta">
        <a href="#" class="save_all" id="twentysixteen_settings_save"><?php esc_html_e('Save All', 'twentysixteen') ?></a>
        <span class="spinner"></span><span class="saved_options"></span>
    </div>
    <div class="theme_settings_inner_content">
        <?php
        $twentysixteen_settings = get_option('twentysixteen_settings', '');
        if (isset($twentysixteen_settings) && '' !== $twentysixteen_settings) {
            foreach ($twentysixteen_settings as $key => $values) {
                echo twentysixteen_build_settings_html('Settings', $values); // WPCS: XSS ok.
            }
        } else {
            echo twentysixteen_build_settings_html('Settings'); // WPCS: XSS ok.
        }
        wp_nonce_field('twentysixteen_settings_nonce_action', 'twentysixteen_settings_nonce'); ?>
    </div>
    </div><?php
}

add_action('wp_ajax_twentysixteen_settings_save', 'twentysixteen_settings_save_callback');
/**
 * Settings save AJAX callback function
 *
 * @return void
 */
function twentysixteen_settings_save_callback() {
    if (isset($_POST['data'], $_POST['twentysixteen_settings_nonce']) && wp_verify_nonce(sanitize_key($_POST['twentysixteen_settings_nonce']), 'twentysixteen_settings_nonce_action') && '' !== $_POST['data']) { // Input var okay.
        $twentysixteen_settings = json_decode(sanitize_text_field(wp_unslash($_POST['data']))); // Input var okay.
        update_option('twentysixteen_settings', $twentysixteen_settings);
    }
    wp_die();
}

add_action('admin_enqueue_scripts', 'twentysixteen_theme_settings_admin_scripts');
/**
 * Enqueue style and scripts for Theme Settings page
 *
 * @since  1.0.0
 *
 * @param  string $hook Page hook suffix where the scripts and styles should be loaded.
 *
 * @return void
 */
function twentysixteen_theme_settings_admin_scripts($hook) {
    $screen = get_current_screen();
    $screen_id = $screen->id;
    if (strpos($screen_id, 'page_twentysixteen_theme') !== false) {
        wp_enqueue_style('twentysixteen_theme_settings_css', get_template_directory_uri() . '/settings/settings.css', '', '1.0.0');
        wp_enqueue_style('wp-color-picker');
        wp_enqueue_media();
        wp_enqueue_script('twentysixteen_theme_settings_js', get_template_directory_uri() . '/settings/settings.js', array('jquery', 'wp-color-picker'), '1.0.0', true);
        wp_localize_script(
            'twentysixteen_theme_settings_js', 'twentysixteen_settings_object', array(
            'saved' => esc_html__('Settings Saved', 'twentysixteen'),
            'delete' => esc_html__('Delete', 'twentysixteen'),
            'save' => esc_html__('Save', 'twentysixteen'),
        ));
    }
}

/**
 * Builds Settings subpage
 *
 * @package    WordPress
 * @subpackage twentysixteen/inc/theme-settings
 * @version    1.0.0
 * @author     Made by Denis <https://madebydenis.com/>
 * @license    https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3
 * @link       https://twentysixteen.io
 * @since      1.0.0
 */
class SettingsSubageBuilder {
    /**
     * Name of the subpage menu
     *
     * @var string $subpage_name The name of the subpage.
     */
    private $subpage_name;
    /**
     * Array of elements to be used in the HTML render of the settings page
     *
     * @var string $subpage_name The name of the subpage.
     */
    private $html_array;

    /**
     * Initialization of the class
     *
     * @param  string $subpage_name The name of the settings page, e.g. Header Settings.
     * @param  array $html_array    The HTML array details for building the settings page.
     */
    public function __construct($subpage_name, $html_array) {
        $this->subpage_name = $subpage_name;
        $this->html_array = $html_array;
        add_action('admin_menu', array($this, 'create_submenu_pages'));
        add_action('wp_ajax_twentysixteen_settings_save_action', array($this, 'twentysixteen_all_settings_save'));
    }

    /**
     * Create submenu page for each settings
     *
     * @return void
     */
    public function create_submenu_pages() {
        $page_hook = 'twentysixteen_theme_' . strtolower(str_replace(' ', '_', $this->subpage_name));
        add_submenu_page('twentysixteen_theme_settings', $this->subpage_name, $this->subpage_name, 'edit_theme_options', $page_hook, array($this, 'twentysixteen_settings_render_subpage'));
    }

    /**
     * Settings control render function
     *
     * This function takes the single control value, and based on what that control is
     * renders the appropriate control.
     *
     * @param  array $default_value Array of the associated default controls with all the details of the controls.
     * @param  array $saved_values  Array of the values from the database.
     *
     * @return string                  HTML of controls.
     */
    public function twentysixteen_theme_settings_control_render($default_value, $saved_values) {
        $name = (isset($default_value['name']) && '' !== $default_value['name']) ? $default_value['name'] : '';
        $control_type = (isset($default_value['control_type']) && '' !== $default_value['control_type']) ? $default_value['control_type'] : '';
        $description = (isset($default_value['description']) && '' !== $default_value['description']) ? '<p class="description">' . $default_value['description'] . '</p>' : '';
        $control_slug = 'twentysixteen_' . str_replace(' ', '_', strtolower($name));
        $control_settings = (isset($saved_values) && '' !== $saved_values) ? $saved_values : '';
        $value = (isset($control_settings[$control_slug]) && '' !== $control_settings[$control_slug]) ? $control_settings[$control_slug] : '';
        $out = '';
        switch ($control_type) {
            case 'input':
                $out .= '<div class="twentysixteen_options_wrapper">
<div class="twentysixteen_options_left">
<h4>' . esc_html($name) . '</h4>
</div>
<div class="twentysixteen_options_right">
<input type="text" name="' . esc_attr($control_slug) . '" value="' . esc_attr($value) . '" class="twentysixteen_setting">
' . wp_kses_post($description) . '
</div>
</div>';
                break;
            case 'image':
                $out .= '<div class="twentysixteen_options_wrapper">
<div class="twentysixteen_options_left">
<h4>' . esc_html($name) . '</h4>
</div>
<div class="twentysixteen_options_right">
<div class="twentysixteen_uploaded_image">
<img src="' . esc_url($value) . '" />
</div>
<input type="text" name="' . esc_attr($control_slug) . '" value="' . esc_url($value) . '" class="twentysixteen_setting twentysixteen_image_upload">
<input type="button" name="image_upload" value="' . esc_html__('Upload Image', 'twentysixteen') . '" class="button upload_image_button">
<input type="button" name="remove_image_upload" value="' . esc_html__('Remove Image', 'twentysixteen') . '" class="button remove_image_button">
' . wp_kses_post($description) . '
</div>
</div>';
                break;
            case 'checkbox':
                $checked = '';
                if (isset($value) && '1' === $value) {
                    $checked = 'checked';
                }
                $out .= '<div class="twentysixteen_options_wrapper">
<div class="twentysixteen_options_left">
<h4>' . esc_html($name) . '</h4>
</div>
<div class="twentysixteen_options_right">
<input type="checkbox" name="' . esc_attr($control_slug) . '" value="' . esc_attr($value) . '" ' . checked($value, 1, false) . ' class="twentysixteen_setting">
<label class="checkbox_label"><span class="twentysixteen_setting checkbox ' . $checked . '"></span>' . esc_html($name) . '</label>
' . wp_kses_post($description) . '
</div>
</div>';
                break;
            case 'colorpicker':
                $out .= '<div class="twentysixteen_options_wrapper">
<div class="twentysixteen_options_left">
<h4>' . esc_html($name) . '</h4>
</div>
<div class="twentysixteen_options_right">
<input type="text" name="' . esc_html($control_slug) . '" value="' . esc_attr($value) . '" class="twentysixteen_setting twentysixteen_color_picker"/>
' . wp_kses_post($description) . '
</div>
</div>';
                break;
            case 'select':
                $out .= '<div class="twentysixteen_options_wrapper">
<div class="twentysixteen_options_left">
<h4>' . esc_html($name) . '</h4>
</div>
<div class="twentysixteen_options_right">
<select class="twentysixteen_setting" name="' . esc_html($control_slug) . '">
<option value=""> -- </option>';
                if (isset($default_value['options']) && '' !== $default_value['options']) {
                    foreach ($default_value['options'] as $option_name => $option_value) {
                        $selected = (isset($value) && '' !== $value && $option_name === $value) ? 'selected="selected"' : '';
                        $out .= '<option value="' . esc_attr($option_name) . '" ' . esc_attr($selected) . ' >' . esc_html($option_value) . '</option>';
                    }
                }
                $out .= '</select>
' . wp_kses_post($description) . '
</div>
</div>';
                break;
            default:
                break;
        }
        return $out;
    }

    /**
     * Rendering function for settings subpage
     *
     * @return void HTML rendered content.
     */
    public function twentysixteen_settings_render_subpage() {
        $settings_array = $this->html_array;
        if (isset($settings_array) && !empty($settings_array)) {
            $set_options = json_decode(json_decode(get_option('twentysixteen_' . str_replace(' ', '_', strtolower($this->subpage_name)), ''))); // Get options from the database.
            ?>
            <div class="wrap">
                <h2 class="page_title"><?php echo esc_html($this->subpage_name); ?></h2>
                <p class="description"><?php echo esc_html($settings_array['description']); ?></p>
                <div class="settings_above_meta">
                    <a href="#" class="save_all" id="twentysixteen_settings_save_subpage"><?php esc_html_e('Save All', 'twentysixteen') ?></a>
                    <span class="spinner"></span><span class="saved_options"></span>
                </div>
                <ul class="theme_settings_inner_content">
                    <?php
                    if (isset($set_options) && '' !== $set_options) {
                        foreach ((array)$set_options as $options_key => $options_value) {
                            ?>
                            <li class="settings_container <?php echo ('0' === $options_key) ? 'show' : '' // WPCS: XSS ok. ?>">
                                <?php
                                foreach ($settings_array['settings_control'] as $control_name => $default_value) {
                                    echo $this->twentysixteen_theme_settings_control_render($default_value, (array)$options_value); // WPCS: XSS ok.
                                }
                                wp_nonce_field('twentysixteen_settings_nonce_action', 'twentysixteen_settings_nonce');
                                ?>
                            </li>
                            <?php
                        }
                    } else {
                        ?>
                        <li class="settings_container show">
                            <?php
                            foreach ($settings_array['settings_control'] as $control_name => $default_value) {
                                echo $this->twentysixteen_theme_settings_control_render($default_value, null); // WPCS: XSS ok.
                            }
                            wp_nonce_field('twentysixteen_settings_nonce_action', 'twentysixteen_settings_nonce');
                            ?>
                        </li>
                        <?php
                    } ?>
                </ul>
            </div>
            <?php
        } else {
            ?>
            <div class="wrap">
                <h2 class="page_title"><?php echo esc_html($this->subpage_name); ?></h2>
                <p class="description"><?php esc_html_e('The options array seems to be empty. Please fill it up with settings to render the page.', 'twentysixteen'); ?></p>
            </div>
            <?php
        }
    }

    /**
     * Settings save AJAX callback function
     *
     * @return void
     */
    public function twentysixteen_all_settings_save() {
        if (isset($_POST['data'], $_POST['twentysixteen_settings_nonce']) && wp_verify_nonce(sanitize_key($_POST['twentysixteen_settings_nonce']), 'twentysixteen_settings_nonce_action') && '' !== $_POST['data']) { // Input var okay.
            $twentysixteen_settings = wp_json_encode(sanitize_text_field(wp_unslash($_POST['data']))); // Input var okay.
            $settings_name = 'twentysixteen_' . str_replace(' ', '_', strtolower($this->subpage_name));
            update_option($settings_name, $twentysixteen_settings);
        }
        wp_die();
    }
}

就这样最简单的设置写出来了,可以集成到插件或者设置选项里!

参考:

Creating Theme Options using object oriented programming (Part 1)
Creating Theme Options using object oriented programming (Part 2)


淮城一只猫

永远年轻,永远热泪盈眶

发表评论

电子邮件地址不会被公开。 必填项已用*标注

我不是机器人*