WordPress限制用户只浏览指定分类文章的完整实现方案

本文介绍了在WordPress网站中实现用户分类访问权限控制的需求与应用场景,如企业内部知识库、在线教育平台等。核心方案是基于用户角色进行权限控制,通过PHP类实现分类过滤、权限检查和管理设置,确保不同用户组只能访问指定的分类内容。

文章作者:曾凤祥
阅读时间: 287 分钟
更新时间:2026年4月15日

一、需求分析与应用场景

在WordPress网站管理中,有时需要限制不同用户组只能浏览或访问特定的分类内容。这种需求常见于:

  1. 企业内部知识库:不同部门只能查看本部门的文档
  2. 在线教育平台:学生只能访问自己购买的课程分类
  3. 多作者博客:作者只能管理自己负责的专栏分类
  4. 会员制网站:不同会员等级访问不同内容分类
  5. 多站点协作:合作伙伴只能查看相关分类的内容

二、核心技术实现方案

2.1 基于用户角色的分类权限控制

<?php
/**
 * WordPress用户分类访问权限控制核心类
 */
class WP_Category_Access_Control {
    
    private $category_restrictions = array();
    
    public function __construct() {
        // 初始化分类权限规则
        $this->init_restrictions();
        
        // 添加必要的钩子
        add_action('pre_get_posts', array($this, 'filter_query_by_category'));
        add_action('template_redirect', array($this, 'check_single_post_access'));
        add_filter('get_terms_args', array($this, 'filter_visible_categories'), 10, 2);
        add_filter('widget_categories_args', array($this, 'filter_category_widget'));
        add_action('admin_menu', array($this, 'add_admin_menu'));
        add_action('admin_init', array($this, 'register_settings'));
        
        // 添加用户权限检查
        add_action('user_register', array($this, 'set_default_category_access'));
        add_action('edit_user_profile', array($this, 'add_user_category_access_fields'));
        add_action('show_user_profile', array($this, 'add_user_category_access_fields'));
        add_action('personal_options_update', array($this, 'save_user_category_access'));
        add_action('edit_user_profile_update', array($this, 'save_user_category_access'));
    }
    
    /**
     * 初始化权限规则配置
     */
    private function init_restrictions() {
        // 从数据库加载配置
        $this->category_restrictions = get_option('category_access_restrictions', array());
        
        // 默认权限规则示例
        $default_rules = array(
            'subscriber' => array(1, 2),  // 订阅者只能访问ID为1,2的分类
            'contributor' => array(1, 2, 3),
            'author' => array(1, 2, 3, 4),
            'editor' => array(),  // 空数组表示可以访问所有分类
            'administrator' => array(),  // 管理员可以访问所有分类
        );
        
        // 如果没有配置,使用默认规则
        if (empty($this->category_restrictions)) {
            update_option('category_access_restrictions', $default_rules);
            $this->category_restrictions = $default_rules;
        }
    }
    
    /**
     * 在文章查询时过滤分类
     */
    public function filter_query_by_category($query) {
        // 不在后台、主查询、非归档页面中应用限制
        if (is_admin() || !$query->is_main_query() || !$query->is_archive()) {
            return;
        }
        
        // 获取当前用户角色
        $user = wp_get_current_user();
        $allowed_categories = $this->get_allowed_categories_for_user($user);
        
        // 如果用户有访问限制
        if (!empty($allowed_categories)) {
            if ($query->is_category() || $query->is_home() || $query->is_search()) {
                // 设置分类查询参数
                $tax_query = array(
                    array(
                        'taxonomy' => 'category',
                        'field'    => 'term_id',
                        'terms'    => $allowed_categories,
                        'operator' => 'IN',
                    )
                );
                
                // 合并现有的tax_query
                $existing_tax_query = $query->get('tax_query');
                if (!empty($existing_tax_query)) {
                    $tax_query = array_merge(array('relation' => 'AND'), $existing_tax_query, $tax_query);
                }
                
                $query->set('tax_query', $tax_query);
            }
        }
        
        return $query;
    }
    
    /**
     * 检查单篇文章访问权限
     */
    public function check_single_post_access() {
        if (!is_single() && !is_singular()) {
            return;
        }
        
        global $post;
        if (!$post) {
            return;
        }
        
        $user = wp_get_current_user();
        $allowed_categories = $this->get_allowed_categories_for_user($user);
        
        // 如果没有限制,允许访问
        if (empty($allowed_categories)) {
            return;
        }
        
        // 检查文章是否在允许的分类中
        $post_categories = wp_get_post_categories($post->ID, array('fields' => 'ids'));
        $has_access = false;
        
        foreach ($post_categories as $category_id) {
            if (in_array($category_id, $allowed_categories)) {
                $has_access = true;
                break;
            }
        }
        
        // 如果没有访问权限,重定向或显示错误
        if (!$has_access) {
            $this->handle_access_denied();
        }
    }
    
    /**
     * 过滤可见分类
     */
    public function filter_visible_categories($args, $taxonomies) {
        if (is_admin() || !in_array('category', $taxonomies)) {
            return $args;
        }
        
        $user = wp_get_current_user();
        $allowed_categories = $this->get_allowed_categories_for_user($user);
        
        if (!empty($allowed_categories)) {
            $args['include'] = $allowed_categories;
        }
        
        return $args;
    }
    
    /**
     * 过滤分类小工具
     */
    public function filter_category_widget($args) {
        $user = wp_get_current_user();
        $allowed_categories = $this->get_allowed_categories_for_user($user);
        
        if (!empty($allowed_categories)) {
            $args['include'] = $allowed_categories;
        }
        
        return $args;
    }
    
    /**
     * 处理无权限访问
     */
    private function handle_access_denied() {
        $redirect_url = apply_filters('category_access_denied_redirect', home_url());
        
        wp_die(
            '<h1>' . __('访问受限', 'textdomain') . '</h1>' .
            '<p>' . __('您没有权限查看此内容。', 'textdomain') . '</p>' .
            '<p><a href="' . esc_url($redirect_url) . '">' . __('返回首页', 'textdomain') . '</a></p>',
            __('访问受限', 'textdomain'),
            array('response' => 403)
        );
        
        // 或者重定向
        // wp_redirect($redirect_url);
        // exit;
    }
    
    /**
     * 获取用户允许访问的分类
     */
    private function get_allowed_categories_for_user($user) {
        // 管理员可以访问所有分类
        if (in_array('administrator', (array) $user->roles)) {
            return array();
        }
        
        $allowed_categories = array();
        
        // 检查用户角色对应的权限
        foreach ($user->roles as $role) {
            if (isset($this->category_restrictions[$role])) {
                $role_categories = $this->category_restrictions[$role];
                if (empty($role_categories)) {
                    return array(); // 空数组表示可以访问所有
                }
                $allowed_categories = array_merge($allowed_categories, $role_categories);
            }
        }
        
        // 检查用户特定的权限覆盖
        $user_specific_categories = get_user_meta($user->ID, '_allowed_categories', true);
        if (!empty($user_specific_categories)) {
            $allowed_categories = $user_specific_categories;
        }
        
        return array_unique($allowed_categories);
    }
    
    /**
     * 添加管理员菜单
     */
    public function add_admin_menu() {
        add_options_page(
            __('分类访问控制', 'textdomain'),
            __('分类访问控制', 'textdomain'),
            'manage_options',
            'category-access-control',
            array($this, 'admin_settings_page')
        );
    }
    
    /**
     * 注册设置
     */
    public function register_settings() {
        register_setting('category_access_control', 'category_access_restrictions');
    }
    
    /**
     * 管理员设置页面
     */
    public function admin_settings_page() {
        ?>
        <div class="wrap">
            <h1><?php _e('分类访问控制设置', 'textdomain'); ?></h1>
            
            <form method="post" action="options.php">
                <?php settings_fields('category_access_control'); ?>
                
                <table class="form-table">
                    <tr>
                        <th scope="row"><?php _e('用户角色权限设置', 'textdomain'); ?></th>
                        <td>
                            <p class="description"><?php _e('为空表示可以访问所有分类。可以设置多个分类ID,用逗号分隔。', 'textdomain'); ?></p>
                            
                            <?php
                            $roles = wp_roles()->get_names();
                            $restrictions = get_option('category_access_restrictions', array());
                            
                            foreach ($roles as $role_slug => $role_name) {
                                $value = isset($restrictions[$role_slug]) ? implode(',', $restrictions[$role_slug]) : '';
                                ?>
                                <p>
                                    <label for="role_<?php echo esc_attr($role_slug); ?>">
                                        <strong><?php echo esc_html($role_name); ?>:</strong>
                                    </label><br>
                                    <input type="text" 
                                           id="role_<?php echo esc_attr($role_slug); ?>" 
                                           name="category_access_restrictions[<?php echo esc_attr($role_slug); ?>]" 
                                           value="<?php echo esc_attr($value); ?>"
                                           class="regular-text"
                                           placeholder="<?php esc_attr_e('例如: 1,2,3', 'textdomain'); ?>">
                                    <span class="description"><?php _e('分类ID,用逗号分隔', 'textdomain'); ?></span>
                                </p>
                                <?php
                            }
                            ?>
                        </td>
                    </tr>
                </table>
                
                <?php submit_button(); ?>
            </form>
        </div>
        <?php
    }
    
    /**
     * 用户注册时设置默认分类权限
     */
    public function set_default_category_access($user_id) {
        $user = get_userdata($user_id);
        
        if ($user && !empty($user->roles)) {
            $role = $user->roles[0];
            $restrictions = get_option('category_access_restrictions', array());
            
            if (isset($restrictions[$role])) {
                update_user_meta($user_id, '_allowed_categories', $restrictions[$role]);
            }
        }
    }
    
    /**
     * 在用户资料页面添加分类访问字段
     */
    public function add_user_category_access_fields($user) {
        if (!current_user_can('edit_users')) {
            return;
        }
        ?>
        <h3><?php _e('分类访问权限', 'textdomain'); ?></h3>
        
        <table class="form-table">
            <tr>
                <th><label for="allowed_categories"><?php _e('允许访问的分类', 'textdomain'); ?></label></th>
                <td>
                    <?php
                    $allowed_categories = get_user_meta($user->ID, '_allowed_categories', true);
                    $category_ids = is_array($allowed_categories) ? implode(',', $allowed_categories) : $allowed_categories;
                    ?>
                    <input type="text" 
                           name="allowed_categories" 
                           id="allowed_categories" 
                           value="<?php echo esc_attr($category_ids); ?>" 
                           class="regular-text"
                           placeholder="<?php esc_attr_e('例如: 1,2,3 (空表示使用角色默认设置)', 'textdomain'); ?>">
                    <p class="description"><?php _e('分类ID,用逗号分隔。此设置会覆盖角色默认设置。', 'textdomain'); ?></p>
                    
                    <div style="margin-top: 10px;">
                        <strong><?php _e('可用分类:', 'textdomain'); ?></strong><br>
                        <?php
                        $categories = get_categories(array('hide_empty' => false));
                        foreach ($categories as $category) {
                            echo '<span style="display: inline-block; margin: 5px; padding: 5px; background: #f5f5f5; border-radius: 3px;">';
                            echo esc_html($category->name) . ' (ID: ' . $category->term_id . ')';
                            echo '</span> ';
                        }
                        ?>
                    </div>
                </td>
            </tr>
        </table>
        <?php
    }
    
    /**
     * 保存用户分类访问设置
     */
    public function save_user_category_access($user_id) {
        if (!current_user_can('edit_user', $user_id)) {
            return false;
        }
        
        if (isset($_POST['allowed_categories'])) {
            $categories = sanitize_text_field($_POST['allowed_categories']);
            $category_ids = array();
            
            if (!empty($categories)) {
                $category_ids = array_map('intval', explode(',', $categories));
                $category_ids = array_unique(array_filter($category_ids));
            }
            
            update_user_meta($user_id, '_allowed_categories', $category_ids);
        }
    }
}

// 初始化
new WP_Category_Access_Control();
?>

2.2 基于用户元数据的细粒度控制

<?php
/**
 * WordPress用户分类权限扩展功能
 */
class WP_User_Category_Permissions {
    
    public function __construct() {
        add_action('init', array($this, 'register_user_category_meta'));
        add_action('pre_get_posts', array($this, 'filter_posts_by_user_categories'));
        add_action('template_redirect', array($this, 'check_post_access'));
        add_filter('get_terms_args', array($this, 'filter_user_categories'), 10, 2);
        add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts'));
    }
    
    /**
     * 注册用户分类元数据
     */
    public function register_user_category_meta() {
        register_meta('user', '_user_allowed_categories', array(
            'type' => 'array',
            'description' => '用户允许访问的分类ID列表',
            'single' => true,
            'show_in_rest' => true,
            'default' => array(),
        ));
    }
    
    /**
     * 过滤用户可访问的分类
     */
    public function filter_user_categories($args, $taxonomies) {
        if (is_admin() || !in_array('category', $taxonomies) || current_user_can('manage_options')) {
            return $args;
        }
        
        $user_id = get_current_user_id();
        if (!$user_id) {
            // 未登录用户可以看到所有分类
            return $args;
        }
        
        $allowed_categories = get_user_meta($user_id, '_user_allowed_categories', true);
        
        if (!empty($allowed_categories) && is_array($allowed_categories)) {
            $args['include'] = $allowed_categories;
        }
        
        return $args;
    }
    
    /**
     * 检查文章访问权限
     */
    public function check_post_access() {
        if (!is_single() && !is_singular()) {
            return;
        }
        
        $post_id = get_the_ID();
        if (!$post_id) {
            return;
        }
        
        $user_id = get_current_user_id();
        if (current_user_can('manage_options')) {
            return; // 管理员可以访问所有内容
        }
        
        $post_categories = wp_get_post_categories($post_id, array('fields' => 'ids'));
        if (empty($post_categories)) {
            return; // 文章没有分类,允许访问
        }
        
        $allowed_categories = get_user_meta($user_id, '_user_allowed_categories', true);
        if (empty($allowed_categories)) {
            $this->redirect_no_access();
        }
        
        $has_access = false;
        foreach ($post_categories as $category_id) {
            if (in_array($category_id, $allowed_categories)) {
                $has_access = true;
                break;
            }
        }
        
        if (!$has_access) {
            $this->redirect_no_access();
        }
    }
    
    /**
     * 处理无权限访问的重定向
     */
    private function redirect_no_access() {
        $redirect_to = apply_filters('user_category_access_denied_redirect', home_url('/access-denied/'));
        
        wp_redirect($redirect_to);
        exit;
    }
    
    /**
     * 前端脚本
     */
    public function enqueue_frontend_scripts() {
        if (is_user_logged_in() && !is_admin()) {
            wp_enqueue_script(
                'user-category-filter',
                plugins_url('js/user-category-filter.js', __FILE__),
                array('jquery'),
                '1.0.0',
                true
            );
            
            wp_localize_script('user-category-filter', 'categoryAccess', array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'user_id' => get_current_user_id(),
                'nonce' => wp_create_nonce('user_category_access_nonce')
            ));
        }
    }
}

三、用户界面实现

3.1 用户权限管理界面

<?php
/**
 * WordPress用户分类权限管理界面
 */
class WP_User_Category_Admin {
    
    public function __construct() {
        add_action('admin_menu', array($this, 'add_admin_pages'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
        add_action('wp_ajax_save_user_categories', array($this, 'ajax_save_user_categories'));
        add_action('wp_ajax_get_user_categories', array($this, 'ajax_get_user_categories'));
    }
    
    /**
     * 添加管理员页面
     */
    public function add_admin_pages() {
        add_users_page(
            __('用户分类权限', 'textdomain'),
            __('分类权限', 'textdomain'),
            'manage_options',
            'user-category-permissions',
            array($this, 'render_admin_page')
        );
        
        add_submenu_page(
            null,
            __('用户分类权限设置', 'textdomain'),
            '',
            'manage_options',
            'user-category-settings',
            array($this, 'render_user_settings_page')
        );
    }
    
    /**
     * 管理员主页面
     */
    public function render_admin_page() {
        if (!current_user_can('manage_options')) {
            wp_die(__('您没有权限访问此页面。', 'textdomain'));
        }
        
        $users = get_users(array(
            'role__not_in' => array('administrator'),
            'orderby' => 'display_name',
            'order' => 'ASC',
        ));
        
        $categories = get_categories(array(
            'hide_empty' => false,
            'orderby' => 'name',
            'order' => 'ASC',
        ));
        ?>
        <div class="wrap">
            <h1><?php _e('用户分类权限管理', 'textdomain'); ?></h1>
            
            <div class="user-category-permissions">
                <div class="user-list-container">
                    <h2><?php _e('用户列表', 'textdomain'); ?></h2>
                    <table class="wp-list-table widefat fixed striped">
                        <thead>
                            <tr>
                                <th><?php _e('用户', 'textdomain'); ?></th>
                                <th><?php _e('角色', 'textdomain'); ?></th>
                                <th><?php _e('允许的分类数', 'textdomain'); ?></th>
                                <th><?php _e('操作', 'textdomain'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($users as $user): ?>
                                <?php
                                $allowed_categories = get_user_meta($user->ID, '_user_allowed_categories', true);
                                $category_count = is_array($allowed_categories) ? count($allowed_categories) : 0;
                                $user_roles = implode(', ', $user->roles);
                                ?>
                                <tr>
                                    <td>
                                        <strong>
                                            <a href="<?php echo get_edit_user_link($user->ID); ?>">
                                                <?php echo esc_html($user->display_name); ?>
                                            </a>
                                        </strong>
                                        <br>
                                        <small><?php echo esc_html($user->user_email); ?></small>
                                    </td>
                                    <td><?php echo esc_html($user_roles); ?></td>
                                    <td>
                                        <span class="category-count"><?php echo $category_count; ?></span>
                                        <?php if ($category_count > 0): ?>
                                            <div class="category-list-preview">
                                                <?php
                                                $category_names = array();
                                                foreach ($allowed_categories as $cat_id) {
                                                    $cat = get_category($cat_id);
                                                    if ($cat) {
                                                        $category_names[] = $cat->name;
                                                    }
                                                }
                                                echo '<small>' . implode(', ', $category_names) . '</small>';
                                                ?>
                                            </div>
                                        <?php endif; ?>
                                    </td>
                                    <td>
                                        <button class="button edit-user-categories" 
                                                data-user-id="<?php echo esc_attr($user->ID); ?>"
                                                data-user-name="<?php echo esc_attr($user->display_name); ?>">
                                            <?php _e('编辑权限', 'textdomain'); ?>
                                        </button>
                                    </td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
                
                <div class="category-list-container" style="margin-top: 30px;">
                    <h2><?php _e('可用分类', 'textdomain'); ?></h2>
                    <div class="category-tags">
                        <?php foreach ($categories as $category): ?>
                            <span class="category-tag" data-category-id="<?php echo esc_attr($category->term_id); ?>">
                                <?php echo esc_html($category->name); ?>
                                <small>(ID: <?php echo $category->term_id; ?>)</small>
                            </span>
                        <?php endforeach; ?>
                    </div>
                </div>
            </div>
        </div>
        
        <!-- 编辑模态框 -->
        <div id="edit-categories-modal" class="hidden">
            <div class="modal-content">
                <h2><?php _e('编辑用户分类权限', 'textdomain'); ?></h2>
                <p class="user-info"></p>
                
                <div class="category-selector">
                    <h3><?php _e('选择允许访问的分类', 'textdomain'); ?></h3>
                    <div class="category-checkboxes">
                        <?php foreach ($categories as $category): ?>
                            <label class="category-checkbox-label">
                                <input type="checkbox" 
                                       name="allowed_categories[]" 
                                       value="<?php echo esc_attr($category->term_id); ?>"
                                       class="category-checkbox">
                                <?php echo esc_html($category->name); ?>
                                <small>(ID: <?php echo $category->term_id; ?>)</small>
                            </label><br>
                        <?php endforeach; ?>
                    </div>
                </div>
                
                <div class="modal-actions">
                    <button type="button" class="button button-primary save-categories">
                        <?php _e('保存设置', 'textdomain'); ?>
                    </button>
                    <button type="button" class="button cancel-modal">
                        <?php _e('取消', 'textdomain'); ?>
                    </button>
                    <span class="spinner"></span>
                </div>
            </div>
        </div>
        
        <style>
        .user-category-permissions {
            margin-top: 20px;
        }
        .category-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 10px;
        }
        .category-tag {
            background: #f0f0f1;
            border: 1px solid #c3c4c7;
            border-radius: 3px;
            padding: 5px 10px;
            font-size: 12px;
        }
        .category-list-preview {
            margin-top: 5px;
            color: #666;
        }
        #edit-categories-modal {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.5);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 99999;
        }
        #edit-categories-modal .modal-content {
            background: white;
            padding: 20px;
            border-radius: 5px;
            max-width: 600px;
            max-height: 80vh;
            overflow-y: auto;
        }
        .category-checkboxes {
            max-height: 300px;
            overflow-y: auto;
            border: 1px solid #ddd;
            padding: 10px;
            margin: 10px 0;
        }
        .category-checkbox-label {
            display: block;
            padding: 5px 0;
        }
        .modal-actions {
            margin-top: 20px;
            display: flex;
            gap: 10px;
            align-items: center;
        }
        </style>
        
        <script>
        jQuery(document).ready(function($) {
            var currentUserId = null;
            
            // 打开编辑模态框
            $('.edit-user-categories').on('click', function() {
                var userId = $(this).data('user-id');
                var userName = $(this).data('user-name');
                
                currentUserId = userId;
                
                // 显示用户信息
                $('.user-info').text('用户: ' + userName);
                
                // 获取用户当前的分类权限
                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'get_user_categories',
                        user_id: userId,
                        nonce: '<?php echo wp_create_nonce('user_category_nonce'); ?>'
                    },
                    beforeSend: function() {
                        $('.spinner').addClass('is-active');
                    },
                    success: function(response) {
                        if (response.success) {
                            // 清空所有复选框
                            $('.category-checkbox').prop('checked', false);
                            
                            // 设置已选中的分类
                            $.each(response.data.categories, function(index, categoryId) {
                                $('.category-checkbox[value="' + categoryId + '"]').prop('checked', true);
                            });
                            
                            // 显示模态框
                            $('#edit-categories-modal').removeClass('hidden');
                        }
                    },
                    complete: function() {
                        $('.spinner').removeClass('is-active');
                    }
                });
            });
            
            // 保存分类权限
            $('.save-categories').on('click', function() {
                var selectedCategories = [];
                $('.category-checkbox:checked').each(function() {
                    selectedCategories.push($(this).val());
                });
                
                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'save_user_categories',
                        user_id: currentUserId,
                        categories: selectedCategories,
                        nonce: '<?php echo wp_create_nonce('user_category_nonce'); ?>'
                    },
                    beforeSend: function() {
                        $('.spinner').addClass('is-active');
                    },
                    success: function(response) {
                        if (response.success) {
                            alert('保存成功!');
                            location.reload();
                        } else {
                            alert('保存失败: ' + response.data.message);
                        }
                    },
                    complete: function() {
                        $('.spinner').removeClass('is-active');
                    }
                });
            });
            
            // 关闭模态框
            $('.cancel-modal').on('click', function() {
                $('#edit-categories-modal').addClass('hidden');
            });
            
            // 点击背景关闭
            $('#edit-categories-modal').on('click', function(e) {
                if (e.target === this) {
                    $(this).addClass('hidden');
                }
            });
        });
        </script>
        <?php
    }
    
    /**
     * AJAX获取用户分类权限
     */
    public function ajax_get_user_categories() {
        check_ajax_referer('user_category_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_die('无权限操作');
        }
        
        $user_id = intval($_POST['user_id']);
        $categories = get_user_meta($user_id, '_user_allowed_categories', true);
        
        if (!is_array($categories)) {
            $categories = array();
        }
        
        wp_send_json_success(array(
            'categories' => $categories
        ));
    }
    
    /**
     * AJAX保存用户分类权限
     */
    public function ajax_save_user_categories() {
        check_ajax_referer('user_category_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array(
                'message' => '无权限操作'
            ));
        }
        
        $user_id = intval($_POST['user_id']);
        $categories = isset($_POST['categories']) ? array_map('intval', $_POST['categories']) : array();
        
        update_user_meta($user_id, '_user_allowed_categories', $categories);
        
        wp_send_json_success(array(
            'message' => '保存成功'
        ));
    }
}

四、短代码与小工具

4.1 用户专属分类列表短代码

<?php
/**
 * 短代码和小工具扩展
 */
class WP_User_Category_Shortcodes {
    
    public function __construct() {
        add_shortcode('user_categories', array($this, 'user_categories_shortcode'));
        add_action('widgets_init', array($this, 'register_widgets'));
    }
    
    /**
     * 用户专属分类列表短代码
     */
    public function user_categories_shortcode($atts) {
        $atts = shortcode_atts(array(
            'show_count' => true,
            'show_empty' => false,
            'orderby' => 'name',
            'order' => 'ASC',
            'style' => 'list', // list, dropdown, grid
            'hide_if_empty' => false,
        ), $atts, 'user_categories');
        
        $user_id = get_current_user_id();
        if (!$user_id) {
            return '<p>' . __('请登录查看分类。', 'textdomain') . '</p>';
        }
        
        $allowed_categories = get_user_meta($user_id, '_user_allowed_categories', true);
        if (empty($allowed_categories) && $atts['hide_if_empty']) {
            return '';
        }
        
        $args = array(
            'taxonomy' => 'category',
            'orderby' => $atts['orderby'],
            'order' => $atts['order'],
            'hide_empty' => !$atts['show_empty'],
        );
        
        if (!empty($allowed_categories)) {
            $args['include'] = $allowed_categories;
        }
        
        $categories = get_categories($args);
        
        if (empty($categories)) {
            return '<p>' . __('没有可用的分类。', 'textdomain') . '</p>';
        }
        
        ob_start();
        
        switch ($atts['style']) {
            case 'list':
                echo '<ul class="user-category-list">';
                foreach ($categories as $category) {
                    $count = $atts['show_count'] ? ' (' . $category->count . ')' : '';
                    echo '<li>';
                    echo '<a href="' . esc_url(get_category_link($category)) . '">';
                    echo esc_html($category->name) . $count;
                    echo '</a>';
                    echo '</li>';
                }
                echo '</ul>';
                break;
                
            case 'dropdown':
                echo '<select class="user-category-dropdown" onchange="if(this.value) window.location.href=this.value">';
                echo '<option value="">' . __('选择分类', 'textdomain') . '</option>';
                foreach ($categories as $category) {
                    $count = $atts['show_count'] ? ' (' . $category->count . ')' : '';
                    echo '<option value="' . esc_url(get_category_link($category)) . '">';
                    echo esc_html($category->name) . $count;
                    echo '</option>';
                }
                echo '</select>';
                break;
                
            case 'grid':
                echo '<div class="user-category-grid">';
                foreach ($categories as $category) {
                    echo '<div class="category-grid-item">';
                    echo '<a href="' . esc_url(get_category_link($category)) . '">';
                    echo '<h4>' . esc_html($category->name) . '</h4>';
                    if ($atts['show_count']) {
                        echo '<span class="category-count">' . $category->count . '篇文章</span>';
                    }
                    if ($category->description) {
                        echo '<p class="category-description">' . esc_html($category->description) . '</p>';
                    }
                    echo '</a>';
                    echo '</div>';
                }
                echo '</div>';
                break;
        }
        
        return ob_get_clean();
    }
    
    /**
     * 注册小工具
     */
    public function register_widgets() {
        register_widget('WP_User_Categories_Widget');
    }
}

/**
 * 用户分类小工具
 */
class WP_User_Categories_Widget extends WP_Widget {
    
    public function __construct() {
        parent::__construct(
            'wp_user_categories',
            __('用户分类列表', 'textdomain'),
            array(
                'description' => __('显示当前用户有权限访问的分类列表', 'textdomain')
            )
        );
    }
    
    public function widget($args, $instance) {
        if (!is_user_logged_in()) {
            return;
        }
        
        $title = !empty($instance['title']) ? apply_filters('widget_title', $instance['title']) : '';
        $style = !empty($instance['style']) ? $instance['style'] : 'list';
        $show_count = !empty($instance['show_count']);
        $show_empty = !empty($instance['show_empty']);
        
        $user_id = get_current_user_id();
        $allowed_categories = get_user_meta($user_id, '_user_allowed_categories', true);
        
        if (empty($allowed_categories)) {
            return;
        }
        
        $categories = get_categories(array(
            'include' => $allowed_categories,
            'hide_empty' => !$show_empty,
        ));
        
        if (empty($categories)) {
            return;
        }
        
        echo $args['before_widget'];
        
        if ($title) {
            echo $args['before_title'] . $title . $args['after_title'];
        }
        
        switch ($style) {
            case 'list':
                echo '<ul>';
                foreach ($categories as $category) {
                        $count = $show_count ? ' <span class="count">(' . $category->count . ')</span>' : '';
                        echo '<li><a href="' . get_category_link($category) . '">' . 
                             esc_html($category->name) . $count . '</a></li>';
                    }
                echo '</ul>';
                break;
                
            case 'dropdown':
                echo '<select onchange="if(this.value) window.location.href=this.value">';
                echo '<option value="">' . __('选择分类', 'textdomain') . '</option>';
                foreach ($categories as $category) {
                    $count = $show_count ? ' (' . $category->count . ')' : '';
                    echo '<option value="' . get_category_link($category) . '">' . 
                         esc_html($category->name) . $count . '</option>';
                }
                echo '</select>';
                break;
        }
        
        echo $args['after_widget'];
    }
    
    public function form($instance) {
        $title = !empty($instance['title']) ? $instance['title'] : __('我的分类', 'textdomain');
        $style = !empty($instance['style']) ? $instance['style'] : 'list';
        $show_count = isset($instance['show_count']) ? (bool) $instance['show_count'] : true;
        $show_empty = isset($instance['show_empty']) ? (bool) $instance['show_empty'] : false;
        ?>
        <p>
            <label for="<?php echo $this->get_field_id('title'); ?>">标题:</label>
            <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" 
                   name="<?php echo $this->get_field_name('title'); ?>" 
                   type="text" value="<?php echo esc_attr($title); ?>">
        </p>
        
        <p>
            <label for="<?php echo $this->get_field_id('style'); ?>">显示样式:</label>
            <select class="widefat" id="<?php echo $this->get_field_id('style'); ?>" 
                    name="<?php echo $this->get_field_name('style'); ?>">
                <option value="list" <?php selected($style, 'list'); ?>>列表</option>
                <option value="dropdown" <?php selected($style, 'dropdown'); ?>>下拉菜单</option>
            </select>
        </p>
        
        <p>
            <input class="checkbox" type="checkbox" 
                   id="<?php echo $this->get_field_id('show_count'); ?>" 
                   name="<?php echo $this->get_field_name('show_count'); ?>" 
                   <?php checked($show_count); ?>>
            <label for="<?php echo $this->get_field_id('show_count'); ?>">显示文章数量</label>
        </p>
        
        <p>
            <input class="checkbox" type="checkbox" 
                   id="<?php echo $this->get_field_id('show_empty'); ?>" 
                   name="<?php echo $this->get_field_name('show_empty'); ?>" 
                   <?php checked($show_empty); ?>>
            <label for="<?php echo $this->get_field_id('show_empty'); ?>">显示空分类</label>
        </p>
        <?php
    }
    
    public function update($new_instance, $old_instance) {
        $instance = array();
        $instance['title'] = !empty($new_instance['title']) ? strip_tags($new_instance['title']) : '';
        $instance['style'] = !empty($new_instance['style']) ? $new_instance['style'] : 'list';
        $instance['show_count'] = isset($new_instance['show_count']) ? (bool) $new_instance['show_count'] : false;
        $instance['show_empty'] = isset($new_instance['show_empty']) ? (bool) $new_instance['show_empty'] : false;
        
        return $instance;
    }
}

五、高级功能扩展

5.1 分类权限的层级继承

<?php
/**
 * 分类层级权限继承
 */
class WP_Category_Hierarchy_Access {
    
    public function __construct() {
        add_filter('user_allowed_categories', array($this, 'include_child_categories'), 10, 2);
        add_action('save_post', array($this, 'check_post_category_access'), 10, 3);
    }
    
    /**
     * 在用户允许的分类中包含子分类
     */
    public function include_child_categories($category_ids, $user_id) {
        if (empty($category_ids)) {
            return $category_ids;
        }
        
        $all_categories = array();
        foreach ($category_ids as $category_id) {
            // 获取分类及其所有子分类
            $children = get_term_children($category_id, 'category');
            if (!is_wp_error($children)) {
                $all_categories = array_merge($all_categories, $children);
            }
            $all_categories[] = $category_id;
        }
        
        return array_unique($all_categories);
    }
    
    /**
     * 检查文章分类权限
     */
    public function check_post_category_access($post_id, $post, $update) {
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }
        
        if (!current_user_can('edit_post', $post_id)) {
            return;
        }
        
        // 只处理已发布文章
        if ($post->post_status !== 'publish') {
            return;
        }
        
        $post_categories = wp_get_post_categories($post_id, array('fields' => 'ids'));
        if (empty($post_categories)) {
            return;
        }
        
        $user_id = get_current_user_id();
        $allowed_categories = get_user_meta($user_id, '_user_allowed_categories', true);
        
        if (empty($allowed_categories)) {
            return; // 用户没有限制
        }
        
        // 检查用户是否有权限发布到这些分类
        $has_permission = false;
        foreach ($post_categories as $category_id) {
            if (in_array($category_id, $allowed_categories)) {
                $has_permission = true;
                break;
            }
        }
        
        if (!$has_permission && !current_user_can('manage_options')) {
            // 移除非权限分类
            $allowed_post_categories = array_intersect($post_categories, $allowed_categories);
            
            if (empty($allowed_post_categories)) {
                // 如果没有权限的分类,设置默认分类
                $default_category = get_option('default_category');
                wp_set_post_categories($post_id, array($default_category));
                
                // 添加管理员通知
                update_post_meta($post_id, '_category_access_warning', 
                    sprintf('文章已被移动到默认分类,您没有权限发布到选择的分类。'));
            } else {
                wp_set_post_categories($post_id, $allowed_post_categories);
            }
        }
    }
}

5.2 REST API支持

<?php
/**
 * WordPress分类权限REST API扩展
 */
class WP_Category_Access_REST_API {
    
    public function __construct() {
        add_action('rest_api_init', array($this, 'register_rest_routes'));
        add_filter('rest_prepare_category', array($this, 'filter_rest_category_response'), 10, 3);
        add_filter('rest_prepare_post', array($this, 'filter_rest_post_response'), 10, 3);
    }
    
    /**
     * 注册REST API路由
     */
    public function register_rest_routes() {
        // 获取用户分类权限
        register_rest_route('category-access/v1', '/user-categories/(?P<id>\d+)', array(
            'methods' => 'GET',
            'callback' => array($this, 'get_user_categories_rest'),
            'permission_callback' => array($this, 'check_rest_permissions'),
        ));
        
        // 设置用户分类权限
        register_rest_route('category-access/v1', '/user-categories/(?P<id>\d+)', array(
            'methods' => 'POST',
            'callback' => array($this, 'set_user_categories_rest'),
            'permission_callback' => array($this, 'check_rest_permissions'),
        ));
        
        // 获取有权限的分类列表
        register_rest_route('category-access/v1', '/allowed-categories', array(
            'methods' => 'GET',
            'callback' => array($this, 'get_allowed_categories_rest'),
            'permission_callback' => function() {
                return is_user_logged_in();
            },
        ));
    }
    
    /**
     * 权限检查
     */
    public function check_rest_permissions($request) {
        $user_id = $request->get_param('id');
        $current_user_id = get_current_user_id();
        
        // 用户只能查看/修改自己的权限,管理员可以查看/修改所有用户
        if ($current_user_id == $user_id || current_user_can('manage_options')) {
            return true;
        }
        
        return new WP_Error('rest_forbidden', __('您没有权限执行此操作。', 'textdomain'), array('status' => 403));
    }
    
    /**
     * 获取用户分类权限
     */
    public function get_user_categories_rest($request) {
        $user_id = $request->get_param('id');
        $categories = get_user_meta($user_id, '_user_allowed_categories', true);
        
        if (!is_array($categories)) {
            $categories = array();
        }
        
        return rest_ensure_response(array(
            'user_id' => $user_id,
            'allowed_categories' => $categories,
            'categories_data' => $this->get_categories_data($categories),
        ));
    }
    
    /**
     * 设置用户分类权限
     */
    public function set_user_categories_rest($request) {
        $user_id = $request->get_param('id');
        $categories = $request->get_param('categories');
        
        if (!is_array($categories)) {
            return new WP_Error('invalid_categories', __('分类数据格式不正确。', 'textdomain'), array('status' => 400));
        }
        
        $categories = array_map('intval', $categories);
        update_user_meta($user_id, '_user_allowed_categories', $categories);
        
        return rest_ensure_response(array(
            'success' => true,
            'message' => __('分类权限已更新。', 'textdomain'),
            'allowed_categories' => $categories,
        ));
    }
    
    /**
     * 获取当前用户有权限的分类
     */
    public function get_allowed_categories_rest() {
        $user_id = get_current_user_id();
        $category_ids = get_user_meta($user_id, '_user_allowed_categories', true);
        
        if (empty($category_ids)) {
            // 如果没有限制,返回所有分类
            $categories = get_categories(array('hide_empty' => false));
        } else {
            $categories = get_categories(array(
                'include' => $category_ids,
                'hide_empty' => false,
            ));
        }
        
        $formatted_categories = array();
        foreach ($categories as $category) {
            $formatted_categories[] = array(
                'id' => $category->term_id,
                'name' => $category->name,
                'slug' => $category->slug,
                'count' => $category->count,
                'description' => $category->description,
                'link' => get_category_link($category),
            );
        }
        
        return rest_ensure_response(array(
            'user_id' => $user_id,
            'categories' => $formatted_categories,
        ));
    }
    
    /**
     * 过滤分类REST响应
     */
    public function filter_rest_category_response($response, $category, $request) {
        $user_id = get_current_user_id();
        if (current_user_can('manage_options')) {
            return $response;
        }
        
        $allowed_categories = get_user_meta($user_id, '_user_allowed_categories', true);
        
        if (!empty($allowed_categories) && !in_array($category->term_id, $allowed_categories)) {
            // 用户没有权限查看此分类
            return new WP_Error('rest_forbidden', __('您没有权限查看此分类。', 'textdomain'), array('status' => 403));
        }
        
        return $response;
    }
    
    /**
     * 过滤文章REST响应
     */
    public function filter_rest_post_response($response, $post, $request) {
        $user_id = get_current_user_id();
        if (current_user_can('manage_options')) {
            return $response;
        }
        
        $post_categories = wp_get_post_categories($post->ID, array('fields' => 'ids'));
        $allowed_categories = get_user_meta($user_id, '_user_allowed_categories', true);
        
        if (empty($allowed_categories)) {
            return $response; // 用户没有限制
        }
        
        $has_access = false;
        foreach ($post_categories as $category_id) {
            if (in_array($category_id, $allowed_categories)) {
                $has_access = true;
                break;
            }
        }
        
        if (!$has_access) {
            return new WP_Error('rest_forbidden', __('您没有权限查看此文章。', 'textdomain'), array('status' => 403));
        }
        
        return $response;
    }
    
    /**
     * 获取分类数据
     */
    private function get_categories_data($category_ids) {
        if (empty($category_ids)) {
            return array();
        }
        
        $categories = get_categories(array(
            'include' => $category_ids,
            'hide_empty' => false,
        ));
        
        $data = array();
        foreach ($categories as $category) {
            $data[] = array(
                'id' => $category->term_id,
                'name' => $category->name,
                'slug' => $category->slug,
                'description' => $category->description,
                'count' => $category->count,
            );
        }
        
        return $data;
    }
}

六、性能优化与缓存策略

<?php
/**
 * 分类权限缓存优化
 */
class WP_Category_Access_Cache {
    
    private $cache_group = 'category_access';
    
    public function __construct() {
        add_action('set_user_role', array($this, 'clear_user_cache'), 10, 3);
        add_action('profile_update', array($this, 'clear_user_cache_on_profile_update'), 10, 2);
        add_action('updated_user_meta', array($this, 'clear_user_cache_on_meta_update'), 10, 4);
        add_action('deleted_user_meta', array($this, 'clear_user_cache_on_meta_update'), 10, 4);
    }
    
    /**
     * 获取缓存的用户分类权限
     */
    public function get_cached_user_categories($user_id) {
        $cache_key = "user_categories_{$user_id}";
        $categories = wp_cache_get($cache_key, $this->cache_group);
        
        if (false === $categories) {
            $categories = get_user_meta($user_id, '_user_allowed_categories', true);
            
            if (!is_array($categories)) {
                $categories = array();
            }
            
            // 包括子分类
            $categories = $this->include_child_categories($categories);
            
            wp_cache_set($cache_key, $categories, $this->cache_group, 12 * HOUR_IN_SECONDS);
        }
        
        return $categories;
    }
    
    /**
     * 获取缓存的用户可访问文章查询
     */
    public function get_cached_user_posts_query($user_id, $query_args = array()) {
        $cache_key = md5("user_posts_{$user_id}_" . serialize($query_args));
        $posts = wp_cache_get($cache_key, $this->cache_group);
        
        if (false === $posts) {
            $allowed_categories = $this->get_cached_user_categories($user_id);
            
            if (!empty($allowed_categories)) {
                $query_args['tax_query'] = array(
                    array(
                        'taxonomy' => 'category',
                        'field' => 'term_id',
                        'terms' => $allowed_categories,
                        'operator' => 'IN',
                    ),
                );
            }
            
            $posts = new WP_Query($query_args);
            wp_cache_set($cache_key, $posts, $this->cache_group, 1 * HOUR_IN_SECONDS);
        }
        
        return $posts;
    }
    
    /**
     * 清理用户缓存
     */
    public function clear_user_cache($user_id, $role, $old_roles) {
        $this->clear_specific_user_cache($user_id);
    }
    
    public function clear_user_cache_on_profile_update($user_id, $old_user_data) {
        $this->clear_specific_user_cache($user_id);
    }
    
    public function clear_user_cache_on_meta_update($meta_id, $user_id, $meta_key, $meta_value) {
        if ($meta_key === '_user_allowed_categories') {
            $this->clear_specific_user_cache($user_id);
        }
    }
    
    /**
     * 清理特定用户的缓存
     */
    private function clear_specific_user_cache($user_id) {
        $cache_key = "user_categories_{$user_id}";
        wp_cache_delete($cache_key, $this->cache_group);
        
        // 清理相关的文章查询缓存
        $pattern = "user_posts_{$user_id}_*";
        if (function_exists('wp_cache_delete_group')) {
            wp_cache_delete_group($this->cache_group);
        }
    }
    
    /**
     * 包含子分类
     */
    private function include_child_categories($category_ids) {
        if (empty($category_ids)) {
            return array();
        }
        
        $all_categories = array();
        foreach ($category_ids as $category_id) {
            $children = get_term_children($category_id, 'category');
            if (!is_wp_error($children)) {
                $all_categories = array_merge($all_categories, $children);
            }
            $all_categories[] = $category_id;
        }
        
        return array_unique($all_categories);
    }
}

七、完整实现示例

<?php
/**
 * WordPress分类访问限制插件主文件
 * Plugin Name: User Category Access Control
 * Plugin URI: https://example.com/user-category-access
 * Description: 限制用户只能浏览指定分类的文章
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL v2 or later
 */

// 防止直接访问
if (!defined('ABSPATH')) {
    exit;
}

// 定义插件常量
define('UCAC_VERSION', '1.0.0');
define('UCAC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('UCAC_PLUGIN_URL', plugin_dir_url(__FILE__));

// 自动加载类文件
spl_autoload_register(function($class) {
    $prefix = 'UCAC_';
    $base_dir = UCAC_PLUGIN_DIR . 'includes/';
    
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        return;
    }
    
    $relative_class = substr($class, $len);
    $file = $base_dir . str_replace('_', '/', $relative_class) . '.php';
    
    if (file_exists($file)) {
        require $file;
    }
});

// 初始化插件
add_action('plugins_loaded', 'ucac_init');

function ucac_init() {
    // 主控制器
    $main = new UCAC_Main_Controller();
    $main->init();
}

// 激活钩子
register_activation_hook(__FILE__, 'ucac_activate');
function ucac_activate() {
    require_once UCAC_PLUGIN_DIR . 'includes/class-activator.php';
    UCAC_Activator::activate();
}

// 停用钩子
register_deactivation_hook(__FILE__, 'ucac_deactivate');
function ucac_deactivate() {
    require_once UCAC_PLUGIN_DIR . 'includes/class-deactivator.php';
    UCAC_Deactivator::deactivate();
}

八、最佳实践与注意事项

8.1 安全考虑

  1. 输入验证:所有用户输入必须经过验证和清理
  2. 权限检查:确保只有授权用户可以修改设置
  3. SQL注入防护:使用WordPress提供的数据库函数
  4. XSS防护:输出时使用适当的转义函数

8.2 性能优化

  1. 缓存策略:合理使用对象缓存
  2. 数据库索引:确保相关字段有适当的索引
  3. 查询优化:避免N+1查询问题
  4. 懒加载:只在需要时加载用户权限数据

8.3 用户体验

  1. 清晰的错误提示:告诉用户为什么无法访问
  2. 友好的界面:管理员界面要直观易用
  3. 批量操作:支持批量设置用户权限
  4. 导入导出:支持权限设置的导入导出

8.4 兼容性考虑

  1. 多站点支持:确保在多站点环境下正常工作
  2. 缓存插件兼容:与常见缓存插件兼容
  3. 其他插件兼容:避免与其他插件冲突
  4. 主题兼容:确保与各种主题兼容

九、总结

通过本文的详细讲解,你应该已经掌握了在WordPress中实现用户分类访问限制的完整方案。从基础的用户角色权限控制,到细粒度的用户级别权限管理,再到高级功能如REST API支持、缓存优化等,这些技术可以灵活组合使用,满足不同场景的需求。

关键点总结:

  1. 核心逻辑:通过pre_get_posts过滤查询,template_redirect检查单篇文章访问
  2. 权限存储:使用用户元数据存储分类权限
  3. 管理界面:提供友好的管理界面
  4. 性能优化:合理使用缓存机制
  5. 扩展性:支持REST API和短代码

根据你的具体需求,可以选择合适的实现方案,或者将这些技术组合使用,打造出符合你网站需求的用户分类访问控制系统。

这篇文章有用吗?

点击星号为它评分!

平均评分 0 / 5. 投票数: 0

到目前为止还没有投票!成为第一位评论此文章。

在AI工具中继续讨论:

曾凤祥

曾凤祥

WordPress技术负责人
小兽WordPress凭借15年的WordPress企业网站开发经验,坚持以“为企业而生的WordPress服务”为宗旨,累计为10万多家客户提供高品质WordPress建站服务,得到了客户的一致好评。我们一直用心对待每一个客户,我们坚信:“善待客户,将会成为终身客户”。小兽WordPress能坚持多年,是因为我们一直诚信。

相关文章

无论你是否已有网站,我们都能帮你把线上业务推上新高度

从0到1,快速搭建专业线上业务平台

从0到1,快速搭建专业线上业务平台

无论您从事何种行业,小兽WordPress​ 都能为您提供专业、可定制、易维护的网站构建方案。我们精选多款高品质模板,适用于展示型官网、品牌形象站、外贸独立站等多种场景,助您快速上线,抢占市场先机。无需代码,轻松启动,即享专业设计。

立即查看所有模版
已有网站?我们来帮你加速、优化、变现

已有网站?我们来帮你加速、优化、变现

您的网站是否遇到加载慢、跳出率高、SEO停滞、体验老旧等问题?这不仅影响技术表现,更会导致客户流失与增长瓶颈。小兽WordPress​ 为您提供全面诊断、提速优化与价值深耕服务,通过“技术+策略”双驱动,助力网站高效转化,推动业务持续增长。

马上获取专属优化方案
微信联系
chat 扫码联系
模板建站
挑选模板
网站定制
免费诊断
咨询热线
咨询热线

189-0733-7671

返回顶部