WordPress限制作者只能管理自己的文章和媒体,并修正统计数据

WordPress用户权限分级已经比较丰富,不过还是有一定局限性。

作者(Author)类型的用户,就开放了过多的权限,虽然不能编辑他人的作品,但依旧能浏览,后台统计数据一览无余,无疑对于网站运营者不太友好。

理想状态自然是注册用户只能管理自己的作品,本文从最常用的文章和媒体入手。

目前没有发现有什么好的插件可以实现这一功能,只好手动完善。

第一步:过滤用户自己的资源

只需要一个简单的钩子即可,【pre_get_posts】。

过滤目标是作者(author),更高权限的编辑、管理员不受此影响。

add_action('pre_get_posts', 'query_set_only_author' );

function query_set_only_author( $wp_query ) {
	global $current_user;

	if( is_admin() && !current_user_can('edit_others_posts') ) {
		$wp_query->set( 'author', $current_user->ID ); // 过滤自己的资源
		add_filter('views_edit-post', 'fix_post_counts'); // 修正文章统计数据
		// add_filter('views_upload', 'fix_media_counts'); // 修正媒体统计数据
	}
}

第二步:修正文章统计数据

统计数据就是后台管理页面上方显示的数字,如图:

代码量很大,原理其实不复杂。

先列举出所有需要的文章状态类型,如【已发布 – publish】、【草稿 – draft】、【回收站-trash】等。

然后创建WP_Query实例,写入查询条件$query,对比当前页面选择的文章状态类型,即$wp_query->query_vars[‘post_status’]。

依次重写存放所有类型的数组$views。

function fix_post_counts($views) {
	global $current_user, $wp_query;
	unset($views['mine']);  // 移除 “我的” 选项
	$types = array(
	    array( 'status' =>  NULL ),
	    array( 'status' => 'publish' ),
	    array( 'status' => 'draft' ),
	    array( 'status' => 'pending' ),
	    array( 'status' => 'trash' )
	  );

	foreach( $types as $type ) {
		$query = array(
		      'author'   => $current_user->ID,
		      'post_type'   => 'post',
		      'post_status' => $type['status']
		    );
		$result = new WP_Query($query);
		if( $type['status'] == NULL ):
		      $class = ($wp_query->query_vars['post_status'] == NULL) ? ' class="current"' : '';
		$views['all'] = sprintf(__('<a href="%s"'. $class .'>All <span class="count">(%d)</span></a>', 'all'),
		        admin_url('edit.php?post_type=post'),
		        $result->found_posts); elseif( $type['status'] == 'publish' ):
		      $class = ($wp_query->query_vars['post_status'] == 'publish') ? ' class="current"' : '';
		$views['publish'] = sprintf(__('<a href="%s"'. $class .'>Published <span class="count">(%d)</span></a>', 'publish'),
		        admin_url('edit.php?post_status=publish&post_type=post'),
		        $result->found_posts); elseif( $type['status'] == 'draft' ):
		       $class = ($wp_query->query_vars['post_status'] == 'draft') ? ' class="current"' : '';
		$views['draft'] = sprintf(__('<a href="%s"'. $class .'>Draft'. ((sizeof($result->posts) > 1) ? "s" : "") .' <span class="count">(%d)</span></a>', 'draft'),
		         admin_url('edit.php?post_status=draft&post_type=post'),
		         $result->found_posts); elseif( $type['status'] == 'pending' ):
		        $class = ($wp_query->query_vars['post_status'] == 'pending') ? ' class="current"' : '';
		$views['pending'] = sprintf(__('<a href="%s"'. $class .'>Pending <span class="count">(%d)</span></a>', 'pending'),
		          admin_url('edit.php?post_status=pending&post_type=post'),
		          $result->found_posts); elseif( $type['status'] == 'trash' ):
		        $class = ($wp_query->query_vars['post_status'] == 'trash') ? ' class="current"' : '';
		$views['trash'] = sprintf(__('<a href="%s"'. $class .'>Trash <span class="count">(%d)</span></a>', 'trash'),
		          admin_url('edit.php?post_status=trash&post_type=post'),
		          $result->found_posts);
		endif;
	}
	return $views;
}

第三步:修正媒体统计数据(可选)

笔者实际使用的WordPress版本是5.8.2,算是当下很新的版本了。

测试时,媒体统计数据已经是正确的,目测无需再修正,所以此部分可以不加。

具体看情况,老版本未测试,如果数量显示不对,不妨一试。这段代码就暂不分析了。

function fix_media_counts($views) {
	global $wpdb, $current_user, $post_mime_types, $avail_post_mime_types;
	$views = array();
	$_num_posts = array();
	$count = $wpdb->get_results( "
    SELECT post_mime_type, COUNT( * ) AS num_posts
    FROM $wpdb->posts
    WHERE post_type = 'attachment'
    AND post_author = $current_user->ID
    AND post_status != 'trash'
    GROUP BY post_mime_type
 ", ARRAY_A );
	foreach( $count as $row )
	    $_num_posts[$row['post_mime_type']] = $row['num_posts'];
	$_total_posts = array_sum($_num_posts);
	$detached = isset( $_REQUEST['detached'] ) || isset( $_REQUEST['find_detached'] );
	if ( !isset( $total_orphans ) )
	    $total_orphans = $wpdb->get_var("
        SELECT COUNT( * )
        FROM $wpdb->posts
        WHERE post_type = 'attachment'
        AND post_author = $current_user->ID
        AND post_status != 'trash'
        AND post_parent < 1
    ");
	$matches = wp_match_mime_types(array_keys($post_mime_types), array_keys($_num_posts));
	foreach ( $matches as $type => $reals )
	    foreach ( $reals as $real )
	      $num_posts[$type] = ( isset( $num_posts[$type] ) ) ? $num_posts[$type] + $_num_posts[$real] : $_num_posts[$real];
	$class = ( empty($_GET['post_mime_type']) && !$detached && !isset($_GET['status']) ) ? ' class="current"' : '';
	$views['all'] = "<a href='upload.php'$class>" . sprintf( __('All <span class="count">(%s)</span>', 'uploaded files' ), number_format_i18n( $_total_posts )) . '</a>';
	foreach ( $post_mime_types as $mime_type => $label ) {
		$class = '';
		if ( !wp_match_mime_types($mime_type, $avail_post_mime_types) )
		      continue;
		if ( !empty($_GET['post_mime_type']) && wp_match_mime_types($mime_type, $_GET['post_mime_type']) )
		      $class = ' class="current"';
		if ( !empty( $num_posts[$mime_type] ) )
		      $views[$mime_type] = "<a href='upload.php?post_mime_type=$mime_type'$class>" . sprintf( translate_nooped_plural( $label[2], $num_posts[$mime_type] ), $num_posts[$mime_type] ) . '</a>';
	}
	$views['detached'] = '<a href="upload.php?detached=1"' . ( $detached ? ' class="current"' : '' ) . '>' . sprintf( __( 'Unattached <span class="count">(%s)</span>', 'detached files' ), $total_orphans ) . '</a>';
	return $views;
}

说明:代码来自一个老外的创作,有兴趣的可查看原文。

参考资料:https://www.collectiveray.com/show-only-posts-media-owned-logged-in-wordpress-user