WordPress插件设置未保存并在提交时重定向到/wp-admin/options.php(生成404)


WordPress plugin settings not saving and getting redirected to /wp-admin/options.php (which generates a 404) on submit

我正在使用 https://github.com/nadeem-khan/WordPress-Plugin-Template 作为入门模板创建一个WordPress。

这是我名为"class-xyz.php"的设置类文件:

<?php
if (!defined('ABSPATH'))
    exit;
class Xyz_Settings {
    /**
     * The single instance of Xyz_Settings.
     * @var     object
     * @access  private
     * @since   1.0.0
     */
    private static $_instance = null;
    /**
     * The main plugin object.
     * @var     object
     * @access  public
     * @since   1.0.0
     */
    public $parent = null;
    /**
     * Prefix for plugin settings.
     * @var     string
     * @access  public
     * @since   1.0.0
     */
    public $base = '';
    /**
     * Available settings for plugin.
     * @var     array
     * @access  public
     * @since   1.0.0
     */
    public $settings = array();
    public function __construct($parent) {
        $this->parent = $parent;
        $this->base = 'xyzsms_';
        // Initialise settings
        add_action('admin_init', array($this, 'init_settings'));
        // Register plugin settings
        add_action('admin_init', array($this, 'register_settings'));
        // Add settings page to menu
        add_action('admin_menu', array($this, 'add_menu_item'));
        // Add settings link to plugins page
        add_filter('plugin_action_links_' . plugin_basename($this->parent->file), array($this, 'add_settings_link'));
    }
    /**
     * Initialise settings
     * @return void
     */
    public function init_settings() {
        $this->settings = $this->settings_fields();
    }
    /**
     * Add settings page to admin menu
     * @return void
     */
    public function add_menu_item() {
        $page = add_options_page(__('XYZ Settings', 'xyz-template'), __('XYZ Settings', 'xyz-template'), 'manage_options', 'xyz_template_settings', array($this, 'settings_page'));
        add_action('admin_print_styles-' . $page, array($this, 'settings_assets'));
    }
    /**
     * Load settings JS & CSS
     * @return void
     */
    public function settings_assets() {
        // We're including the farbtastic script & styles here because they're needed for the colour picker
        // If you're not including a colour picker field then you can leave these calls out as well as the farbtastic dependency for the wpt-admin-js script below
        wp_enqueue_style('farbtastic');
        wp_enqueue_script('farbtastic');
        // We're including the WP media scripts here because they're needed for the image upload field
        // If you're not including an image upload then you can leave this function call out
        wp_enqueue_media();
        wp_register_script($this->parent->_token . '-settings-js', $this->parent->assets_url . 'js/settings' . $this->parent->script_suffix . '.js', array('farbtastic', 'jquery'), '1.0.0');
        wp_enqueue_script($this->parent->_token . '-settings-js');
    }
    /**
     * Add settings link to plugin list table
     * @param  array $links Existing links
     * @return array        Modified links
     */
    public function add_settings_link($links) {
        $settings_link = '<a href="options-general.php?page=xyz_template_settings">' . __('Settings', 'xyz-template') . '</a>';
        array_push($links, $settings_link);
        return $links;
    }
    /**
     * Build settings fields
     * @return array Fields to be displayed on settings page
     */
    private function settings_fields() {
        $settings['social-bar-position'] = array(
            'title' => __('Bar Position', 'xyz-plugin-bar'),
            'fields' => array(
                array(
                    'id' => 'xyz_position',
                    'label' => __('Position', 'xyz-plugin-bar'),
                    'description' => __('Select the position', 'xyz-plugin-bar'),
                    'type' => 'radio',
                    'options' => array('left' => 'Left', 'right' => 'Right'),
                    'default' => 'left'
                ),
                array(
                    'id' => 'xyz_margin_top',
                    'label' => __('Bar Margin Top', 'xyz-plugin-bar'),
                    'description' => __('Define the margin top', 'xyz-plugin-bar'),
                    'type' => 'text',
                    'placeholder' => __('Enter margin.', 'xyz-plugin-bar'),
                    'default' => '200px'
                )
            )
        );
        $settings['social-networks-bar'] = array(
            'title' => __('Social Networks', 'xyz-plugin-bar'),
            'fields' => array(
                array(
                    'id' => 'xyz_networks',
                    'label' => __('Social', 'xyz-plugin-bar'),
                    'description' => __('Select social.', 'xyz-plugin-bar'),
                    'type' => 'checkbox_multi',
                    'options' => array(
                        'facebook' => 'Facebook'
                    )
                )
            )
        );
        $settings['links-bar'] = array(
            'title' => __('Links', 'xyz-plugin-bar'),
            'description' => __('Enter the Urls for all the selected social networks:', 'xyz-plugin-bar'),
            'fields' => array(
                array(
                    'id' => 'facebook_url',
                    'label' => __('Facebook URL', 'xyz-plugin-bar'),
                    'description' => __('Enter the URL for Facebook.', 'xyz-plugin-bar'),
                    'type' => 'text',
                    'placeholder' => __('Enter Facebook URL', 'xyz-plugin-bar'),
                    'default' => ''
                )
            )
        );
        $settings['custom-classes-bar'] = array(
            'title' => __('Custom Classes', 'xyz-plugin-bar'),
            'description' => __('Add custom classes in the main parent container of the bar (will affect shortcodes as well):', 'xyz-plugin-bar'),
            'fields' => array(
                array(
                    'id' => 'xyz_custom_class',
                    'label' => __('Add Custom Classes in the Bar Parent Container'),
                    'description' => __('Add your own custom CSS class (like customclass1 customclass2). Leave empty if default styling is desired.', 'xyz-plugin-bar'),
                    'type' => 'textarea',
                    'default' => '',
                    'placeholder' => __('Add custom classes like .custom_class1 .custom_class2 .custom_class3 to the main container of the social bar.', 'xyz-plugin-bar')
                )
            )
        );
        $settings['social-bar-custom-colors'] = array(
            'title' => __('Bar Icons Colors', 'xyz-plugin-bar'),
            'description' => __('Select the colors', 'xyz-plugin-bar'),
            'fields' => array(
                array(
                    'id' => 'bar_facebook_icon_custom_color',
                    'label' => __('Facebook Icon Color', 'xyz-plugin-bar'),
                    'description' => __('Default color: #FEFEFE ', 'xyz-plugin-bar'),
                    'type' => 'color',
                    'default' => '#FEFEFE'
                )
            )
        );

        $settings['add-custom-css-bar'] = array(
            'title' => __('Add CSS', 'xyz-plugin-bar'),
            'label' => __('Add CSS', 'xyz-plugin-bar'),
            'description' => __('Paste your CSS', 'xyz-plugin-bar'),
            'fields' => array(
                array(
                    'id' => 'xyz_add_custom_css',
                    'label' => __('Custom CSS', 'wordpress'),
                    'description' => __('Do not style', 'xyz-plugin-bar'),
                    'type' => 'textarea',
                    'default' => '',
                    'placeholder' => __('Do not style .', 'xyz-plugin-bar')
                )
            )
        );
        $settings = apply_filters('xyz_fields', $settings);
        return $settings;
    }
    /**
     * Register plugin settings
     * @return void
     */
    public function register_settings() {
        if (is_array($this->settings)) {
            foreach ($this->settings as $section => $data) {
                // Add section to page
                add_settings_section($section, $data['title'], array($this, 'settings_section'), 'xyz_template_settings');
                foreach ($data['fields'] as $field) {
                    // Validation callback for field
                    $validation = '';
                    if (isset($field['callback'])) {
                        $validation = $field['callback'];
                    }
                    // Register field
                    $option_name = $this->base . $field['id'];
                    register_setting('xyz_template_settings', $option_name, $validation);
                    // Add field to page
                    add_settings_field($field['id'], $field['label'], array($this, 'display_field'), 'xyz_template_settings', $section, array('field' => $field));
                }
            }
        }
    }
    public function settings_section($section) {
        $html = '<p> ' . $this->settings[$section['id']]['description'] . '</p>' . "'n";
        echo $html;
    }
    /**
     * Generate HTML for displaying fields
     * @param  array $args Field data
     * @return void
     */
    public function display_field($args) {
        $field = $args['field'];
        $html = '';
        $option_name = $this->base . $field['id'];
        $option = get_option($option_name);
        $data = '';
        if (isset($field['default'])) {
            $data = $field['default'];
            if ($option) {
                $data = $option;
            }
        }
        switch ($field['type']) {
            case 'text':
            case 'password':
            case 'number':
                $html .= '<input id="' . esc_attr($field['id']) . '" type="' . $field['type'] . '" name="' . esc_attr($option_name) . '" placeholder="' . esc_attr($field['placeholder']) . '" value="' . $data . '"/>' . "'n";
                break;
            case 'text_secret':
                $html .= '<input id="' . esc_attr($field['id']) . '" type="text" name="' . esc_attr($option_name) . '" placeholder="' . esc_attr($field['placeholder']) . '" value=""/>' . "'n";
                break;
            case 'textarea':
                $html .= '<textarea id="' . esc_attr($field['id']) . '" rows="5" cols="50" name="' . esc_attr($option_name) . '" placeholder="' . esc_attr($field['placeholder']) . '">' . $data . '</textarea><br/>' . "'n";
                break;
            case 'checkbox':
                $checked = '';
                if ($option && 'on' == $option) {
                    $checked = 'checked="checked"';
                }
                $html .= '<input id="' . esc_attr($field['id']) . '" type="' . $field['type'] . '" name="' . esc_attr($option_name) . '" ' . $checked . '/>' . "'n";
                break;
            case 'checkbox_multi':
                foreach ($field['options'] as $k => $v) {
                    $checked = false;
                    if (in_array($k, $data)) {
                        $checked = true;
                    }
                    $html .= '<label for="' . esc_attr($field['id'] . '_' . $k) . '"><input type="checkbox" ' . checked($checked, true, false) . ' name="' . esc_attr($option_name) . '[]" value="' . esc_attr($k) . '" id="' . esc_attr($field['id'] . '_' . $k) . '" /> ' . $v . '</label> ';
                }
                break;
            case 'radio':
                foreach ($field['options'] as $k => $v) {
                    $checked = false;
                    if ($k == $data) {
                        $checked = true;
                    }
                    $html .= '<label for="' . esc_attr($field['id'] . '_' . $k) . '"><input type="radio" ' . checked($checked, true, false) . ' name="' . esc_attr($option_name) . '" value="' . esc_attr($k) . '" id="' . esc_attr($field['id'] . '_' . $k) . '" /> ' . $v . '</label> ';
                }
                break;
            case 'select':
                $html .= '<select name="' . esc_attr($option_name) . '" id="' . esc_attr($field['id']) . '">';
                foreach ($field['options'] as $k => $v) {
                    $selected = false;
                    if ($k == $data) {
                        $selected = true;
                    }
                    $html .= '<option ' . selected($selected, true, false) . ' value="' . esc_attr($k) . '">' . $v . '</option>';
                }
                $html .= '</select> ';
                break;
            case 'select_multi':
                $html .= '<select name="' . esc_attr($option_name) . '[]" id="' . esc_attr($field['id']) . '" multiple="multiple">';
                foreach ($field['options'] as $k => $v) {
                    $selected = false;
                    if (in_array($k, $data)) {
                        $selected = true;
                    }
                    $html .= '<option ' . selected($selected, true, false) . ' value="' . esc_attr($k) . '" />' . $v . '</label> ';
                }
                $html .= '</select> ';
                break;
            case 'image':
                $image_thumb = '';
                if ($data) {
                    $image_thumb = wp_get_attachment_thumb_url($data);
                }
                $html .= '<img id="' . $option_name . '_preview" class="image_preview" src="' . $image_thumb . '" /><br/>' . "'n";
                $html .= '<input id="' . $option_name . '_button" type="button" data-uploader_title="' . __('Upload an image', 'xyz-template') . '" data-uploader_button_text="' . __('Use image', 'xyz-template') . '" class="image_upload_button button" value="' . __('Upload new image', 'xyz-template') . '" />' . "'n";
                $html .= '<input id="' . $option_name . '_delete" type="button" class="image_delete_button button" value="' . __('Remove image', 'xyz-template') . '" />' . "'n";
                $html .= '<input id="' . $option_name . '" class="image_data_field" type="hidden" name="' . $option_name . '" value="' . $data . '"/><br/>' . "'n";
                break;
            case 'color':
                ?><div class="color-picker" style="position:relative;">
                    <input type="text" name="<?php esc_attr_e($option_name); ?>" class="color" value="<?php esc_attr_e($data); ?>" />
                    <div style="position:absolute;background:#FFF;z-index:99;border-radius:100%;" class="colorpicker"></div>
                </div>
                <?php
                break;
        }
        switch ($field['type']) {
            case 'checkbox_multi':
            case 'radio':
            case 'select_multi':
                $html .= '<br/><span class="description">' . $field['description'] . '</span>';
                break;
            default:
                $html .= '<label for="' . esc_attr($field['id']) . '"><span class="description">' . $field['description'] . '</span></label>' . "'n";
                break;
        }
        echo $html;
    }
    /**
     * Validate individual settings field
     * @param  string $data Inputted value
     * @return string       Validated value
     */
    public function validate_field($data) {
        if ($data && strlen($data) > 0 && $data != '') {
            $data = urlencode(strtolower(str_replace(' ', '-', $data)));
        }
        return $data;
    }
    /**
     * Load settings page content
     * @return void
     */
    public function settings_page() {
        // Build page HTML
        $html = '<div class="wrap" id="xyz_settings">' . "'n";
        $html .= '<h2>' . __('XYZ Settings', 'xyz-template') . '</h2>' . "'n";
        $html .= '<form method="post" action="options.php" enctype="multipart/form-data">' . "'n";
        // Setup navigation
        $html .= '<ul id="settings-sections" class="subsubsub hide-if-no-js">' . "'n";
        $html .= '<li><a class="tab all current" href="#all">' . __('All', 'xyz-template') . '</a></li>' . "'n";
        foreach ($this->settings as $section => $data) {
            $html .= '<li>| <a class="tab" href="#' . $section . '">' . $data['title'] . '</a></li>' . "'n";
        }
        $html .= '</ul>' . "'n";
        $html .= '<div class="clear"></div>' . "'n";
        // Get settings fields
        ob_start();
        settings_fields('xyz_template_settings');
        do_settings_sections('xyz_template_settings');
        $html .= ob_get_clean();
        $html .= '<p class="submit">' . "'n";
        $html .= '<input name="Submit" type="submit" class="button-primary" value="' . esc_attr(__('Save Settings', 'xyz-template')) . '" />' . "'n";
        $html .= '</p>' . "'n";
        $html .= '</form>' . "'n";
        $html .= '</div>' . "'n";
        echo $html;
    }
    /**
     * Main Xyz_Settings Instance
     *
     * Ensures only one instance of Xyz_Settings is loaded or can be loaded.
     *
     * @since 1.0.0
     * @static
     * @see Xyz()
     * @return Main Xyz_Settings instance
     */
    public static function instance($parent) {
        if (is_null(self::$_instance)) {
            self::$_instance = new self($parent);
        }
        return self::$_instance;
    }
// End instance()
    /**
     * Cloning is forbidden.
     *
     * @since 1.0.0
     */
    public function __clone() {
        _doing_it_wrong(__FUNCTION__, __('Cheatin&#8217; huh?'), $this->parent->_version);
    }
// End __clone()
    /**
     * Unserializing instances of this class is forbidden.
     *
     * @since 1.0.0
     */
    public function __wakeup() {
        _doing_it_wrong(__FUNCTION__, __('Cheatin&#8217; huh?'), $this->parent->_version);
    }
// End __wakeup()
}

所有设置字段都正确显示,但一旦按下提交按钮,就会生成 404,并且不会保存任何设置。我在这里错过了什么?

更新:以下是初始化Xyz_Settings类在另一个文件中的初始化方式:

function Xyz_bar_init () {
    $instance = Xyz_bar::instance( __FILE__, '1.0.0' );
    if( is_null( $instance->settings ) ) {
        $instance->settings = Xyz_Settings::instance( $instance );
    }
    return $instance;
}
Xyz_bar_init();
更新(2

):我已经激活了我自己的插件的一个非常精简的版本,只有 2-3 个设置选项,它似乎工作得很好(因为值在数据库中正确保存/检索)。设置选项的剪切数是否会导致具有 200 多个选项的完整插件出现问题(请注意,我使用的是资源有限的共享主机)?

如果要保存大量选项,请确保不要饱和 PHP 5.3.9 中引入的 max_input_vars 限制,以限制接受的输入变量的数量 - 来自 PHP 文档:

可以接受多少输入变量(限制适用于 $_GET, $_POST 和 $_COOKIE 分别超全球)。此指令的使用 减轻使用哈希的拒绝服务攻击的可能性 碰撞。

您应该能够在 php.ini 中根据需要修改此值。

另外,请记住检查错误日志。

我对此文件进行了更改,现在帖子显示:- 类-wordpress-plugin-template-post_type.php您的代码:-

// Post type name and labels
        $this->post_type = 'post_type';
        $this->plural = _x( 'Posts', 'post type general name' , 'wordpress-plugin-template' );
        $this->single = _x( 'Post', 'post type singular name' , 'wordpress-plugin-template' );

我的代码:-

// Post type name and labels
        $this->post_type = 'post_type1'; // change your value here
        $this->plural = _x( 'Your Posts', 'post type general name' , 'wordpress-plugin-template' );
        $this->single = _x( 'Post', 'post type singular name' , 'wordpress-plugin-template' );

转到管理员/永久链接,只需再次保存您的设置,无需更改任何内容。 10 满分 9 这将解决添加自定义设置后的 404 问题(与自定义模板相同)。

您还可以在构造函数的末尾添加 'flush_rewrite_rules(); ' 以避免此步骤。

[编辑]好了,现在就这样了...您的表单操作仅指向"选项.php"。这意味着您的插件将在插件或根文件夹中查找选项.php。应该是site_url()."/wp-admin/options.php".