news 2026/9/17 2:20:45

OpenProject 用户属性(User attributes)管理完全指南:扩展用户档案、自定义字段与映射配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenProject 用户属性(User attributes)管理完全指南:扩展用户档案、自定义字段与映射配置

OpenProject 用户属性(User attributes)管理完全指南:扩展用户档案、自定义字段与映射配置

【免费下载链接】openprojectOpenProject is the leading open source project management software for product, project and portfolio management. A powerful Jira alternative with agile planning, issue tracking, roadmaps, Gantt charts, time tracking, collaboration features, and more. Available on premises or in the cloud. ⭐ Star us on GitHub项目地址: https://gitcode.com/GitHub_Trending/op/openproject

User attributes(用户属性)是 OpenProject 提供的一项管理能力,允许管理员在标准用户档案之外扩展额外的信息维度,例如职位头衔(Job title)、技能、证书或使用语言。这些属性可以显示在用户档案、用户悬浮卡片(hover card)以及资源管理模块的用户卡片上。本文将以官方系统管理指南为主体,结合仓库源码深入讲解如何查看、创建、分组、编辑与删除用户属性,并详解 Mapped attributes(映射属性)的配置原理,帮助管理员与二次开发者完整掌握这一功能模块。

一、什么是用户属性:内置属性与自定义属性

在 OpenProject 中,用户属性本质上是一种面向用户(User)实体、作用域为全局的自定义字段(Custom Field)。从源码结构看,它的实现模型为UserCustomField,该类继承自核心的CustomField,并混入了CustomField::Sectionable(分节能力)模块,同时通过belongs_to :user_custom_field_section与分节模型UserCustomFieldSection建立关联。

内置属性

OpenProject 提供以下不可删除的内置用户属性:

  • Username(用户名)
  • First name(名字)
  • Last name(姓氏)
  • Email(电子邮箱)
  • Language(语言)
  • Department(部门)

在源码中,这些内置属性由UserCustomFieldSection::BUILT_IN_ATTRIBUTES常量直接定义:%w[login firstname lastname mail language department]。内置属性只能调整其在列表中的顺序(上移、下移、置顶、置底),不能编辑或删除。

自定义属性

你还可以创建任意数量的自定义用户属性,例如:

  • Job title(职位头衔)
  • Spoken languages(使用语言)
  • Key skills(关键技能)
  • Job start date(入职日期)

自定义属性除排序外,还支持完整的编辑与删除操作。

二、查看用户属性列表

进入管理后台:Administration settings(管理设置)→ Users and permissions(用户与权限)→ User attributes(用户属性)

在该页面可以:

  • 创建与管理自定义用户属性
  • 将属性分组到不同的 Section(分区)中
  • 选择哪些属性显示在用户卡片上

从源码角度看,该页面由Admin::Settings::UserCustomFieldsController#index提供支撑,其对应的系统测试位于 spec/features/admin/custom_fields/users/index_spec.rb,测试中验证了非管理员无法访问该页面(返回 "You are not authorized to access this page."),并校验了分区与属性的显示顺序。

用户属性页面分为两个标签页:

  • Attributes(属性):列出所有已配置的用户属性,属性被组织进各个 Section(分区)中
  • Mapped attributes(映射属性):将特定的语义用途(如 Job title)绑定到某个自定义属性上

三、Attributes 标签页:属性与分区管理

3.1 属性行的组成

Attributes标签页中,每个属性行包含:

  1. 拖拽手柄(Drag handle)
  2. 用户属性名称
  3. 格式(Format)
  4. More(…)菜单图标

3.2 创建与管理分区(Sections)

分区用于将属性分组展示。添加分区的步骤:

  1. 点击右上角的+ Add按钮
  2. 选择Section

随后为分区命名并保存。

  • 通过分区标题右侧的More菜单,可以对分区执行重命名、删除或重新排序
  • 属性可以在分区之间拖拽移动;整个分区也可以借助分区名左侧的拖拽手柄进行拖拽排序

[!TIP] 分区只有在不包含任何属性时才能被删除。

[!TIP] 属性在所有用户档案中始终显示在它被分配到的分区内。

从源码层看,分区的增删改排序分别由UserCustomFieldSectionsControllercreateupdatedestroymovedrop动作驱动,底层调用UserCustomFieldSections目录下的CreateServiceUpdateServiceDeleteService等服务对象,并通过 Turbo Stream 无刷新更新页面。其中DeleteService继承自::BaseServices::Delete(delete_service.rb);而模型层has_many :custom_fields, dependent: :restrict_with_exception(user_custom_field_section.rb)保证了"非空分区禁止删除"这一约束。功能测试 index_spec.rb 亦明确验证了:分区下仍有属性时删除按钮被禁用(aria-disabled='true'),清空属性后才可以删除。

3.3 用户属性格式(User attribute formats)

创建用户属性时,需要从以下格式中选择一种:

格式说明
Boolean布尔值,以复选框形式呈现,勾选或不勾选
Date日期,通过日期选择器(date picker)选取
Float有理数(小数)
Hierarchy(企业版附加组件)层级结构选择,可选择一个或多个层级列表项;结构在属性创建后的Items标签页中维护
Integer整数
List平面列表选项
Text文本格式,可限制长度
Long text长文本,适用于需要输入较长内容的场景

不同格式会提供不同的附加参数,例如最小/最大宽度、默认值或用于校验的正则表达式。

[!IMPORTANT] 用户属性一旦创建,无法更改其格式

3.4 创建用户属性

创建新属性的步骤:

  1. 点击右上角+ Add按钮
  2. 选择User attribute
  3. 从可用格式列表中选择一种用户属性格式

以下是一个List格式的用户属性创建示例:

表单中的关键配置项:

  • Name(名称):用户属性在用户档案及其他展示位置显示的名称
  • Section(分区):如果已创建分区,可选择该属性归属的分区
  • Allow multi-select(允许多选):允许用户为该字段赋予多个值
  • Required(必填):勾选后启用该属性并使其对所有用户为必填;无法在单个用户层面取消激活。已有用户在进行更新时不会被强制要求补填该值
  • Admin-only(仅管理员可见):启用后该属性仅对管理员可见,其他用户看不到
  • Editable(可编辑):允许用户自行编辑该属性
  • Show on user card(显示在用户卡片上):在用户悬浮卡片(悬停于用户名上时显示的信息)中展示该属性

[!IMPORTANT]Boolean类型的用户属性不能被设置为必填。

属性创建完成后,还可以继续为其定义帮助文本以及(针对 List/Hierarchy 等格式)维护选项列表。

3.5 Hierarchy 层级用户属性(企业版附加组件)

类型为Hierarchy的用户属性,其行为与Hierarchy类型的工作包自定义字段完全一致。详细信息请参阅工作包自定义字段文档。该功能受特性开关custom_field_hierarchies控制(对应企业版附加组件),其选项结构由Admin::Settings::UserCustomFields::Hierarchy::ItemsController管理。

3.6 编辑、排序与删除用户属性

编辑自定义属性

  1. 选择More(…)菜单
  2. 选择Edit
  3. 按需更新属性设置

重新排序用户属性:属性按照本页面配置的顺序显示。通过属性旁的More(…)菜单可以选择:

  • Move to top(移至顶部)
  • Move up(上移)
  • Move down(下移)
  • Move to bottom(移至底部)

源码层面,排序动作由UserCustomFieldsController#move(调用CustomFields::MoveService)与#drop(调用CustomFields::DropService,支持跨分区拖放)实现;分区内的字段顺序由attribute_order与分区的position共同驱动(参见UserCustomFieldSection.with_filled_fields_for中按attribute_order过滤排序的逻辑)。

删除用户属性:只有自定义用户属性可以被删除。

  1. 选择More(…)菜单
  2. 选择Delete
  3. 确认删除

删除操作会将该属性从所有用户档案中永久移除。

3.7 定义用户属性帮助文本

点击某个用户属性,进入Help text标签页,可以为属性定义:

  • Caption(标题):一段短文本,作为属性标题展示以提供上下文
  • Help text(帮助文本):一段较长文本,当用户悬停在属性名旁的问号图标上时显示,用于提供更详细的解释。这是必填字段
  • Attachments(附件):附加文件或图片以图解该用户属性

[!IMPORTANT] 此处添加的任何文本与附件,对所有已登录用户均可见

帮助文本与附件功能由AttributeHelpTextActions等共享 Concern 提供支撑,对应控制器动作attribute_help_text/update_attribute_help_text(user_custom_fields_controller.rb),并有专门的系统测试 spec/features/admin/settings/user_custom_fields/attribute_help_text_spec.rb 覆盖。

四、Mapped attributes 标签页:为属性赋予语义用途

Mapped attributes(映射属性)标签页允许你将特定的语义用途(Purpose)指派给某个自定义用户属性。

映射规则:

  • 每个用途(Purpose)只能指派给一个用户属性
  • 每个用户属性只能映射到一个用途

当前 OpenProject 支持的映射属性如下:

用途说明
Job title(职位头衔)在多个位置(如用户卡片)将每个用户的职位头衔显示在其姓名旁

映射操作步骤:

  1. 打开Mapped attributes标签页
  2. Job title下拉列表中选择一个自定义用户属性
  3. 点击Save

[!NOTE] 只有自定义用户属性可以被映射,内置属性不在可选范围内。

映射机制的源码实现

从源码结构看,"映射"在数据层由UserCustomField模型上的semantic_key字段承载:

enum :semantic_key, { job_title: "job_title" }, prefix: true validates :semantic_key, uniqueness: { scope: :type }, allow_nil: true scope :with_semantic_key, ->(semantic_key) { where(semantic_key:) } def self.for_semantic_key(semantic_key) with_semantic_key(semantic_key).first end
  • semantic_key是一个枚举字段,目前仅定义了job_title一个值,prefix: true会生成semantic_key_job_title形式的辅助方法
  • uniqueness: { scope: :type }唯一性校验,正是"每个用途只能映射一个用户属性"这一规则在数据库层面的保证

管理端界面由UserCustomFieldsController#indextab == "semantic_keys"时加载:

if params[:tab] == "semantic_keys" @semantic_key_field_options = UserCustomField.order(:position) @semantic_key_assignments = UserCustomField.where.not(semantic_key: nil).index_by(&:semantic_key) end

保存映射则走update_semantic_keys动作(user_custom_fields_controller.rb),其内部assign_semantic_keys在事务中先将原持有者清空、再写入新的映射,以始终满足(type, semantic_key)的唯一索引约束。

五、属性的显示与消费:悬浮卡片与用户档案

用户属性配置完成后,会通过以下途径在系统中展示:

  1. 用户档案(User profile):属性按所属分区、分区内按顺序渲染
  2. 用户悬浮卡片(Hover card):鼠标悬停用户名时弹出,仅显示勾选了Show on user card已有值的属性
  3. 资源管理模块的用户卡片:参考资源管理用户指南

从源码看,悬浮卡片的渲染由Users::HoverCardComponent完成,其核心调用为:

def card_sections_with_fields @card_sections_with_fields ||= UserCustomFieldSection.with_filled_fields_for(@user, visible_on_user_card: true) end

对应的模型查询方法UserCustomFieldSection.with_filled_fields_for做了三件关键事情:

  • 仅取当前用户可见(UserCustomField.visible(User.current))的属性;
  • 可选地只保留visible_on_user_card: true(即勾选了"显示在用户卡片上")的属性;
  • 通过user.custom_values.where.not(value: [nil, ""])过滤出已填写值的属性,再按分区与attribute_order排序输出——因此悬浮卡片上不会出现空属性占位。

多值字段在卡片上以 Primer Label 标签形式渲染,超出上限时显示 "and N more" 溢出提示(hover_card_component.rb)。相关测试位于 spec/components/users/hover_card_component_spec.rb 与 spec/components/users/profile/attributes_section_component_spec.rb。

六、常见问题与注意事项汇总

  • 格式不可变:用户属性创建后无法修改格式,创建前需确认所选格式。
  • 内置属性不可删除:内置属性(Username、First name、Last name、Email、Language、Department)只能排序,不能编辑或删除。
  • 非空分区不可删除:删除分区前必须先将其中所有属性移走或删除(dependent: :restrict_with_exception约束 + 前端禁用双重保障)。
  • Boolean 不可设为必填:布尔型属性存在此限制。
  • 必填不追溯:将属性设为 Required 后,已有用户更新时不会被强制要求补填。
  • 映射互斥:每个 Purpose 只能绑定一个自定义属性,反之亦然;映射仅针对自定义属性。
  • 可见性边界:Admin-only 属性仅管理员可见;帮助文本与附件对所有已登录用户可见,发布前请注意内容敏感性。

七、相关文档与代码入口

  • 官方文档:用户属性(本指南)
  • 关联文档:工作包自定义字段(含 Hierarchy 类型)
  • 模型实现:app/models/user_custom_field.rb、app/models/user_custom_field_section.rb
  • 控制器实现:app/controllers/admin/settings/user_custom_fields_controller.rb、app/controllers/admin/settings/user_custom_field_sections_controller.rb
  • 服务层:app/services/user_custom_field_sections/
  • 组件层:app/components/users/hover_card_component.rb
  • 测试用例:spec/features/admin/custom_fields/users/index_spec.rb、spec/features/admin/settings/user_custom_fields/attribute_help_text_spec.rb、spec/components/users/hover_card_component_spec.rb

通过本文,管理员可以在 OpenProject 中为团队构建丰富且结构化的用户档案体系:用分区组织属性、用多种格式适配数据、用帮助文本降低填写歧义,并通过 Mapped attributes 让自定义属性承担系统级语义(如 Job title),从而在用户卡片与档案中呈现统一的个人信息视图。

【免费下载链接】openprojectOpenProject is the leading open source project management software for product, project and portfolio management. A powerful Jira alternative with agile planning, issue tracking, roadmaps, Gantt charts, time tracking, collaboration features, and more. Available on premises or in the cloud. ⭐ Star us on GitHub项目地址: https://gitcode.com/GitHub_Trending/op/openproject

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 2:19:07

基于STM32的自动售货机设计:主控、电机驱动与状态机实现

简介:基于STM32的自动售货机完整工程项目,包含源代码与文档说明,适合电子信息、自动化、计算机等专业学生用于课程设计、毕业设计或项目初期演示。项目基于STM32标准库开发,覆盖定时器、Flash、RCC、ADC、I2C、CAN、USART等常用外…

作者头像 李华
网站建设 2026/9/17 2:18:59

SAR ADC冗余位原理与工程实践:提升DNL/INL可靠性的关键技术

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 2:13:45

国产PLM选型指南:从研发数据管理到落地避坑全解析

1. 选型先想清楚:你真的需要PLM,还是需要把设计流程理顺做PLM选型这些年,我见过太多企业一上来就问“哪个国产PLM系统最好”,开口就要对比厂商名单。但真正走进车间和研发部看一圈下来,一半以上的企业其实还没到选型阶…

作者头像 李华
网站建设 2026/9/17 2:12:56

明细语义层与MQL:NoETL时代的业务分析新范式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华