Drupal 7中的每个节点有多个编辑器
Drupal的伟大之处在于它灵活的用户权限系统。我们都熟悉的开箱即用权限网格涵盖了控制用户能做什么和不能做什么的大多数用例。模块开发人员也很容易创建新的权限和角色来限制他们实现的逻辑。
然而,我遇到过一个实际用例,其中默认配置选项是不够的。也就是说,如果您需要多个用户有权编辑给定类型的特定节点,但他们不一定有权编辑相同类型的其他节点。换句话说,下一篇伟大的文章应该由劳拉和格伦编辑,而不是由他们的同事编辑。但是,特定角色的用户可以是他们自己的内容的主人,也可以是某种类型的所有内容的主人。所以这不可能马上实现。
在本文中,我将通过一个简单的自定义模块的形式向您展示我对这个问题的解决方案editor_list
.文章节点将有一个字段,您可以在其中选择用户,并且只有这些用户(或具有完全访问权限的用户)能够编辑该特定节点。您可以在这里找到模块git存储库您可以在您的网站上安装它,以便快速启动。请记住它依赖于实体引用我们马上就会看到。
为了节省空间,我将尽量减少代码注释,但如果您愿意,可以在存储库中找到它们。在本教程的其余部分中,假定您对Drupal 7有基本的了解。
脚手架
我们首先需要editor_list.info
我们的模块文件,让我们继续:
name =编辑器列表描述=模块说明了在一个节点上拥有多个编辑器的自定义解决方案。核心= 7。X dependencies[] = entityreference
接下来,我们需要我们的editor_list.module
文件,我们的大部分业务逻辑将位于其中。继续创建它,我们会填充它。
最后,虽然这里没有讲到,但我们可以editor_list.install
文件,我们可以实现hook_install ()
而且hook_update
用于创建字段和/或部署配置的钩子。在存储库中,您将发现我提供了一个安装钩子,它已经创建了一个名为field_editors
并将其附加到Article内容类型。如果您正在进行操作,但没有使用存储库中的代码,则应该继续操作,通过UI手动创建字段。这是一个简单的字段,引用User实体,并允许无限的选择。没有什么专业。
节点访问
回到我们的.module
文件,是时候实现我们的访问逻辑了。首先,为了使事情尽可能地灵活和可重用,让我们有一个简单的函数,它返回一个节点类型数组,我们应用我们的访问逻辑:
函数editor_list_node_types(){返回数组(“文章”);}
因为我们只针对文章,这就足够了。但是我们会在很多地方使用这个函数,所以如果我们需要目标其他类型,我们只需要更新这个数组。
接下来,让我们编写另一个有用的函数,该函数返回给定节点的editors字段中设置的所有用户id。我们也会在很多地方用到它:
函数editor_list_uids_from_list(美元的节点){美元的用户=field_get_items(“节点”,美元的节点,“field_editors”);allowed_uids美元=数组();如果(美元的用户){allowed_uids美元=到(函数($ user){返回$ user[“target_id”];},美元的用户);}返回allowed_uids美元;}
我相信这个函数是不言自明的,所以我不会在这里详细介绍。相反,我们可以转向我们的hook_node_access ()
每当用户尝试执行某个操作时,Drupal就会调用它某物使用节点(查看、编辑或删除):
/** *实现hook_node_access()* /函数editor_list_node_access(美元的节点,op美元,美元账户){node_type美元=editor_list_node_types();如果(!is_object(美元的节点)||!in_array(美元的节点->类型,node_type美元)||op美元= = !“更新”){返回NODE_ACCESS_IGNORE;}allowed_uids美元=editor_list_uids_from_list(美元的节点);如果(空(allowed_uids美元)){返回NODE_ACCESS_IGNORE;}如果(in_array(美元账户->uid,allowed_uids美元)){返回NODE_ACCESS_ALLOW;}}
这里发生了什么?
首先,我们使用前面声明的helper函数来获取我们想要目标的节点类型列表,如果当前访问节点的节点类型不在我们的列表中,或者如果用户尝试的操作不是“update”类型,我们基本上忽略这种情况并返回。然后,我们使用另一个helper函数检查编辑器列表中是否有此节点的用户,如果没有则再次忽略这种情况。但是,如果存在,并且我们的访问用户是其中之一,则返回NODE_ACCESS_ALLOW
常量,基本上允许用户访问执行尝试的操作。就是这样。
你可以查看文档有关此钩子如何工作的更多信息。
假设您拥有可以创建和编辑任何类型内容的管理用户和不能编辑文章(可能除了他们自己创建的文章之外)的常规认证用户。将后一种用户中的一个添加到节点的编辑器列表中将使他们能够访问该特定节点。另一件很棒的事情是,由于这些都是很好的集成,上下文过滤器和选项卡也考虑到这些动态权限。
字段访问
我们现在有了一个工作模块,它完成了我最初设定的任务。但是,假设您的管理用户是唯一负责向编辑器列表添加用户的用户。换句话说,您担心如果您的编辑器可以编辑它们的节点并将自己从列表中删除,那么它们将被锁定在它们应该工作的节点之外。
为了解释这种情况,我们需要实现字段访问检查,并消除编辑人员篡改该字段的可能性。实现hook_field_access应该能很好地达到目的。如果你想知道,这个钩子类似于hook_node_access ()
但它负责单个字段,而不是整个节点(+两个其他小差异)。
/** *实现hook_field_access()。* /函数editor_list_field_access(op美元,美元的领域,entity_type美元,美元的实体,美元账户){node_type美元=editor_list_node_types();如果(entity_type美元= = =“节点”& &is_object(美元的实体)& &in_array(美元的实体->类型,node_type美元)){返回editor_list_control_field_access(op美元,美元的领域,entity_type美元,美元的实体,美元账户);}}
这就是它。还有一些参数,因为这个钩子是为所有实体调用的,而不仅仅是节点。但是,我们再次检查当前访问的节点是否是我们之前定义的节点之一(并且实体实际上是一个节点),这一次委托给另一个函数以保持事情更整洁:
函数editor_list_control_field_access(op美元,美元的领域,entity_type美元,美元的实体,美元账户){如果(op美元= = !“编辑”){返回;}uid美元=editor_list_uids_from_list(美元的实体);如果(!in_array(美元账户->uid,uid美元)){返回;}否认美元=数组(“field_editors”);如果(in_array(美元的领域[“field_name”],否认美元)){返回假;}}}
因为我们只关心用户是否在尝试更新一个特定的字段,如果不是这样,我们什么也不返回。请记住这里的op字符串是编辑
而不是更新
就像在另一个钩子里一样。这只是我们都遇到的Drupal不一致的怪癖之一爱这么多。和前面一样,如果当前用户不是编辑器列表的一部分,我们将忽略这种情况。
然后,我们定义一个希望拒绝访问的字段名数组(在我们的例子中只有一个,但我们可以根据用例添加它)。最后,如果当前访问的字段是变量的一部分,则返回false否认美元
数组中。这里的另一个不同之处在于,我们必须返回一个布尔值,而不是像之前那样返回一个常量。
现在,给定节点列表中的编辑器不能删除自己,也不能将任何人添加到列表中。但是,在某些情况下,您可能需要这个功能,而在其他情况下则不需要。由你决定。
整理
我在这里要展示的最后一件事与组织和用户体验有关。对于我们当前的实现,Article节点上的编辑器列表字段是存在的的某个地方在表单上(编辑字段设置时拖放它的位置)。然而,如果它自动成为编写信息
在页面底部的组?就像这样:
我想是的。我们来看看怎么做。
首先,我们需要实现hook_form_alter
或者它的一个变体。我更喜欢最有针对性的一个,以避免不必要的调用它和一堆条件检查:
/** *实现hook_form_BASE_FORM_ID_alter()* /函数editor_list_form_article_node_form_alter(&美元的形式,&form_state美元,form_id美元){美元的形式[“# after_build”][]=“editor_list_node_form_after_build”;}
我们选择了BASE_FORM_ID
所以如果我们将应用程序扩展到其他类型,我们也会对它们做同样的事情。在里面,我们只定义an# after_build
函数将在表单完成构建时触发。这是为了确保所贡献的模块已经完成了所有表单更改。剩下要做的就是编写负责修改表单的函数:
函数editor_list_node_form_after_build(美元的形式,&form_state美元){美元的领域=field_info_field(“field_editors”);如果(!field_access(“编辑”,美元的领域,“节点”,美元的形式[“#实体”])){返回美元的形式;}如果(美元的形式[“作者”][“#访问”]= = =0){返回美元的形式;}field_editors美元=美元的形式[“field_editors”];field_editors美元[“#重量”]=0;美元的形式[“作者”][“additional_authors”]=field_editors美元;美元的形式[“field_editors”]=数组();返回美元的形式;}
这看起来很复杂,但实际上并不复杂。我们首先加载编辑器列表字段的字段定义。这样我们就可以运行field_access
检查它,如果当前用户没有访问字段的权限,只返回表单数组不变。对象的访问权限,执行同样的操作作者
在表单上进行分组(这是编写信息
组,我们想把字段放入)。最后,我们创建字段定义的副本,更改其权重并将其放入组中,然后重新设置原始定义以避免重复。
差不多就是这样了。现在,编辑器列表字段应该与与作者身份相关的其他信息隐藏在一起。
结论
在本文中,我们为Drupal 7无法立即解决的内容编辑问题创建了一个解决方案。但是,它确实为我们提供了必要的开发工具,可以在自定义模块中轻松完成这一任务。
现在,article节点表单上有了一个编辑器列表字段,可以通过该字段准确地指定哪些用户可以访问该特定节点。但是请记住,为了使其有用,您添加到这些列表中的用户不能拥有允许他们编辑所有文章节点的角色。否则你不会看到太大的区别。