社区新闻

如何在 WordPress 中禁用特定区块

查看官方原文 ↗ 发布于

你是否曾想过在 WordPress 中禁用特定区块?无论是为了简化编辑体验、保持站点一致性,还是其他原因,本文将指导你完成整个过程。 

在 WordPress 中,限制区块的核心可以通过 PHP 或 JavaScript 来实现。选择哪种方式很大程度上取决于你现有的站点设置和个人偏好。

例如,如果你已经为了创建自定义区块变体或其他编辑器增强功能而排队加载了一个 JavaScript 文件,那么扩展你的 JavaScript 代码以包含区块限制可能是最无缝的途径。另一方面,PHP 提供了一种服务器端解决方案,可以轻松添加到环境中已有的插件和主题中。

无论选择哪种方式,你都能控制编辑器中可用的区块。让我们开始吧。

配置个人偏好

虽然本文主要关注使用代码禁用区块,但也有无需代码的选项。你可以使用编辑器的偏好设置面板来隐藏特定的区块类型。

要在编辑器或站点编辑器中执行此操作,请点击选项菜单(屏幕右上角的三个垂直点)。从该菜单中选择偏好设置。在出现的弹出窗口中,导航到侧边栏的区块部分。在这里,你将看到已注册区块的列表。勾选你希望隐藏的区块。

这些用户特定的设置提供了一种便捷的方式来根据个人偏好定制区块插入器,特别是如果你有一些从不使用的区块。然而,这样做不会限制站点上其他用户的区块。为此,我们需要一些代码。

使用 PHP 禁用区块

第一种方法使用 PHP,特别是 allowed_block_types_all 钩子。此钩子的回调函数接受两个参数:

  • $allowed_block_types – 一个区块类型标识符的数组,或一个布尔值用于启用或禁用所有区块。
  • $block_editor_context – 当前的区块编辑器上下文。

需要注意的是,使用此钩子禁用区块时,任何由已禁用区块组成的区块模式也将对用户不可用。 

使用允许列表

下面是一个简单的示例,它将只允许用户插入标题、列表、图像和段落区块。这可以视为一个“允许列表”。所有其他区块都被禁用。 

要测试以下代码,请将其添加到主题的 functions.php 文件中,或将其包含在插件中。

/**
 * 过滤区块编辑器中允许的区块类型列表。
 *
 * 此函数将可用区块类型限制为仅标题、图像、列表和段落。
 *
 * @param array|bool $allowed_block_types 区块类型标识符数组,或布尔值用于启用/禁用所有。
 * @param object     $block_editor_context 当前的区块编辑器上下文。
 *
 * @return array 允许的区块类型数组。
 */
function example_allowed_block_types( $allowed_block_types, $block_editor_context ) {

	$allowed_block_types = array(
		'core/heading',
		'core/image',
		'core/list',
		'core/list-item',
		'core/paragraph',
	);

	return $allowed_block_types;
}
add_filter( 'allowed_block_types_all', 'example_allowed_block_types', 10, 2 );

应用此允许列表后,区块插入器的外观如下。 

区块插入器显示有限的区块选项。

让我们通过添加一些条件语句使这个示例更贴近实际。

你可能只想将作者限制为这些区块类型,但给予编辑和管理员访问所有内容的权限。你可以使用 current_user_can() 函数来实现。使用以下代码进行更新。

/**
 * 根据用户权限过滤区块编辑器中允许的区块类型列表。
 *
 * 此函数对没有 'publish_pages' 权限的用户,将可用区块类型限制为仅标题、图像、列表和段落。
 *
 * @param array|bool $allowed_block_types 区块类型标识符数组,或布尔值用于启用/禁用所有。
 * @param object     $block_editor_context 当前的区块编辑器上下文。
 *
 * @return array 允许的区块类型数组。
 */
function example_allowed_block_types_for_authors( $allowed_block_types, $block_editor_context ) {

	// 如果当前用户没有正确的权限,则限制区块。
	if ( ! current_user_can( 'publish_pages' ) ) {
		$allowed_block_types = array(
			'core/heading',
			'core/image',
			'core/list',
			'core/list-item',
			'core/paragraph',
		);

		return $allowed_block_types;
	}

	// 用户拥有正确的权限,因此允许所有区块。
	return true;
}
add_filter( 'allowed_block_types_all', 'example_allowed_block_types_for_authors', 10, 2 );

有关检查角色和权限的更多信息,请参阅插件手册。 

到目前为止,我们还没有使用 $block_editor_context 参数。这允许你确定编辑器的特征,例如正在编辑的文章类型、用户是在编辑器还是站点编辑器中工作等等。 

让我们修改代码,在编辑文章时限制所有用户可插入的区块,但仅限于用户在编辑器中时。如果编辑其他文章类型或在站点编辑器中工作,则应启用所有区块。 

对于文章类型标识符,你可以使用 $block_editor_context->post->post_type,而 $block_editor_context->name 将返回编辑器的类型。可能的值有:

  • core/edit-post – 编辑器
  • core/edit-site – 站点编辑器
  • core/widgets – 小工具编辑器
  • core/customize-widgets – 自定义器中的小工具编辑器
/**
 * 根据编辑上下文过滤允许的区块类型列表。
 *
 * 当用户在编辑器中编辑文章时,此函数将可用区块类型限制为标题、图像、列表和段落。
 * 当编辑其他文章类型或在站点编辑器中时,允许所有区块。
 *
 * @param array|bool $allowed_block_types 区块类型标识符数组,或布尔值用于启用/禁用所有。
 * @param object     $block_editor_context 当前的区块编辑器上下文,包括编辑器类型和正在编辑的文章。
 *
 * @return array|bool 编辑文章时允许的区块类型数组,或其他上下文中允许所有区块时返回 true。
 */
function example_allowed_block_types_when_editing_posts( $allowed_block_types, $block_editor_context ) {

	// 仅在编辑器中编辑文章时应用。
	if ( 
		'core/edit-post' === $block_editor_context->name &&
		isset( $block_editor_context->post ) && 
		'post' === $block_editor_context->post->post_type
	) {
		$allowed_block_types = array(
			'core/heading',
			'core/image',
			'core/list',
			'core/list-item',
			'core/paragraph',
		);

		return $allowed_block_types;
	}

	// 在站点编辑器或编辑其他文章类型时允许所有区块。
	return true;
}
add_filter( 'allowed_block_types_all', 'example_allowed_block_types_when_editing_posts', 10, 2 );

如果你的站点使用了设计为仅包含特定类型内容的自定义文章类型,这种方法会很有帮助。

使用禁止列表

到目前为止,我们提供的是“允许”区块的列表。虽然这在某些情况下效果很好,但在其他场景中,你可能希望禁用特定的区块。也许你的 WordPress 实例包含一百多个区块,而你只想禁用其中几个。 

实现“禁止”列表在 JavaScript 中需要的代码更少(我们将在本文后面介绍),但仍然可以使用 PHP 的 allowed_block_types_all 钩子来完成。这里的关键是编程式地获取所有可用的已注册区块类型,并移除你想要禁用的那些。

要实现这一点,请使用 WP_Block_Type_Registery 类和 get_all_registered() 方法。 

考虑以下示例,为没有 edit_theme_options 权限的用户禁用导航和查询区块。除非你自定义了 WordPress 安装,否则这通常只包括管理员。

/**
 * 根据用户权限过滤允许的区块类型列表。
 *
 * 此函数检查当前用户是否具有 'edit_theme_options' 权限。
 * 如果用户没有此权限,则从编辑器中允许的区块类型列表中移除某些区块。
 *
 * @param array|bool $allowed_block_types 区块类型标识符数组,或布尔值用于启用/禁用所有。
 * @param object     $block_editor_context 当前的区块编辑器上下文。
 *
 * @return array 过滤后的允许区块类型列表。如果当前用户没有 'edit_theme_options' 权限,该列表将排除被禁止的区块。
 */
function example_disallow_block_types( $allowed_block_types, $block_editor_context ) {

	// 如果当前用户没有正确的权限,则禁止区块。
	if  ( ! current_user_can( 'edit_theme_options' ) ) {
		$disallowed_blocks = array(
			'core/navigation',
			'core/query',
		);
		
		// 如果 $allowed_block_types 尚未设置或为空,则获取所有已注册区块。
		if ( ! is_array( $allowed_block_types ) || empty( $allowed_block_types ) ) {
			$registered_blocks   = WP_Block_Type_Registry::get_instance()->get_all_registered();
			$allowed_block_types = array_keys( $registered_blocks );
		}

		// 为允许的区块创建一个新数组。
		$filtered_blocks = array();

		// 遍历允许区块列表中的每个区块。
		foreach ( $allowed_block_types as $block ) {

			// 检查该区块是否不在禁止区块列表中。
			if ( ! in_array( $block, $disallowed_blocks, true ) ) {

				// 如果未被禁止,则将其添加到过滤列表中。
				$filtered_blocks[] = $block;
			}
		}

		// 返回过滤后的允许区块列表
		return $filtered_blocks;
	}
	
	return $allowed_block_types;
}
add_filter( 'allowed_block_types_all', 'example_disallow_block_types', 10, 2 );

与前面的示例类似,你可以根据各种因素(如特定的文章类型或编辑器)进一步调整这种方法。

这展示了单个 PHP 钩子在自定义用户可用区块范围方面的强大功能。现在,让我们专注于使用 JavaScript。

使用 JavaScript 禁用区块

通过 JavaScript 实现区块限制需要更多的设置,因为它涉及在编辑器中创建并排队加载一个 JavaScript 文件。

在本指南中,我假设重点是向主题添加区块变体。但是,你可以调整此方法以在插件中使用。主要区别在于函数的文件位置。让我们首先创建一个名为 restrict-blocks.js 的文件,并将其放在主题的 assets/js 文件夹中。

接下来,使用 enqueue_block_editor_assets 钩子和标准的 wp_equeue_script() 函数来排队加载 restrict-blocks.js。除非你有更高级的设置,否则请将此代码插入主题的 functions.php 文件中。

function example_enqueue_block_restrictions() {
	wp_enqueue_script(
		'example-enqueue-block-variations',
		get_template_directory_uri() . '/assets/js/restrict-blocks.js',
		array( 'wp-blocks', 'wp-dom-ready' ),
		wp_get_theme()->get( 'Version' ),
		false
	);
}
add_action( 'enqueue_block_editor_assets', 'example_enqueue_block_restrictions' );

请注意,wp-blockswp-dom-ready 被列为依赖项。这些将在后续步骤中创建限制时使用,但初始设置已完成。

使用允许列表

在上面的 PHP 部分中,创建允许列表比禁止列表更简单。在 JavaScript 中,情况正好相反。 

区块使用 unregisterBlockType 函数来注销,该函数接受区块的名称。此函数来自之前添加到排队加载函数中的 wp-blocks 依赖项,可以通过在函数前加上 wp.block 来调用,或者像这样:

wp.domReady( () => {

	// 注销诗歌区块。
	wp.blocks.unregisterBlockType( 'core/verse' );
} );

要选择性地注销区块,只留下少数几个,首先使用 getBlockTypes 函数获取所有现有区块类型的列表。一旦有了这个列表,你就可以遍历它,并注销未包含在你指定的“允许列表”中的每个区块。以下是编写此功能的方法:

wp.domReady( () => {

	// 仅允许标题、图像、列表和段落区块。
	const allowedBlocks = [
    		'core/heading',
    		'core/image',
    		'core/list',
    		'core/paragraph',
	];

	wp.blocks.getBlockTypes().forEach( function ( blockType ) {
		if ( allowedBlocks.indexOf( blockType.name ) === -1 ) {
			wp.blocks.unregisterBlockType( blockType.name );
		}
	} );
} );

与 PHP 方法非常相似,你也可以添加条件语句。例如,来自 @wordpress/data 包的 canUser 选择器允许你处理权限,而 @wordpress/editor 包则提供了一个方便的 getCurrentPostType 选择器。

一个简化的代码片段展示了如何使用 select 来检索这两个选择器。此代码允许你识别当前文章类型,并查看用户是否可以更新设置(表示管理员权限)。

const { canUser } = wp.data.select( 'core' );
const { getCurrentPostType } = wp.data.select( 'core/editor' );

// 检查用户权限并获取当前文章类型。
const canUserUpdateSettings = canUser( 'update', 'settings' );
const currentPostType = getCurrentPostType();

然而,为了使用 select,你必须使用 wp.domReady 以外的替代方法。否则,数据存储将不会返回任何数据。

一种方法是使用 <a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-plugins/#plugins-api">wp.plugins.registerPlugin</a> 注册一个编辑器插件。通常插件用于在各种编辑器插槽中渲染组件,但它们不一定非要这样使用。

以下是如何在用户不是管理员且正在编辑文章时应用这些区块限制。 

wp.plugins.registerPlugin( 'example-allow-blocks-by-post-type-and-user-permissions', {
	render: () => {

        // 将区块限制为以下列表。
        const allowedBlocks = [
    		'core/heading',
    		'core/image',
    		'core/list',
    		'core/paragraph',
        ];

        // 检查用户权限并获取当前文章类型。
        const canUserUpdateSettings = wp.data.select( 'core' ).canUser( 'update', 'settings' );
        const currentPostType = wp.data.select( 'core/editor' ).getCurrentPostType();
        
        // 确保值确实已定义。
        if ( canUserUpdateSettings === undefined || currentPostType === undefined ) {
            return null;
        }

        // 如果用户不是管理员且当前文章类型为 'post',则注销允许列表之外的所有区块。
        if ( ! canUserUpdateSettings && currentPostType === 'post' ) {
            wp.blocks.getBlockTypes().forEach( function ( blockType ) {
                if ( allowedBlocks.indexOf( blockType.name ) === -1 ) {
                    wp.blocks.unregisterBlockType( blockType.name );
                }
            } );
        }
        
        // 该插件仅用于禁用区块,因此无需渲染任何内容。
        return null;
    },
} );

使用禁止列表

在需要仅禁用少数几种区块类型的情况下,使用禁止列表可能是最直接的方法。例如,如果你希望限制非管理员用户在编辑文章时使用导航和查询区块,可以这样构建代码:

wp.plugins.registerPlugin( 'example-disallow-blocks-by-post-type-and-user-permissions', {
	render: () => {

        // 禁用以下列表中的区块。
        const disallowedBlocks = [
    		'core/navigation',
    		'core/query',
	    ];

        // 检查用户权限并获取当前文章类型。
        const canUserUpdateSettings = wp.data.select( 'core' ).canUser( 'update', 'settings' );
        const currentPostType = wp.data.select( 'core/editor' ).getCurrentPostType();
        
        // 确保值确实已定义。
        if ( canUserUpdateSettings === undefined || currentPostType === undefined ) {
            return null;
        }

        // 如果用户不是管理员且当前文章类型为 'post',则注销允许列表之外的所有区块。
        if ( ! canUserUpdateSettings && currentPostType === 'post' ) {
            disallowedBlocks.forEach( function( blockType ) {
                wp.blocks.unregisterBlockType( blockType );
            } );
        }

        // 该插件仅用于禁用区块,因此无需渲染任何内容。
        return null;
    },
} );

JavaScript 提供了与 PHP 相当的灵活性,在某些情况下甚至可能超过它。这是因为 JavaScript 直接在编辑器内部运行,可以访问编辑器环境特有的额外设置和功能。然而,深入探讨这些高级功能超出了本文的范围。 

最终,最佳方法——JavaScript 还是 PHP——取决于你的具体设置和需求。

禁用区块变体

虽然本文主要关注在