罗琰锴头像
关注

OpenProject 项目入门实战:从打开、创建到项目层级,源码级解析项目工作区机制

OpenProject 项目入门实战:从打开、创建到项目层级,源码级解析项目工作区机制

【免费下载链接】openproject OpenProject 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 【免费下载链接】openproject 项目地址: https://gitcode.com/GitHub_Trending/op/openproject

本文以 OpenProject 官方文档《Projects introduction》为主体,系统讲解项目(Project)这一 OpenProject 中一切协作活动的容器:如何在“全部项目”下拉菜单中按层级打开已有项目,如何创建空白项目、基于模板创建项目或走 Enterprise 项目启动申请流程,以及项目标识符(identifier)的自动建议与校验规则、子项目层级、创建后成员归属等细节。读完本文,你不仅能完成项目的全生命周期基础操作,还能对照 app/models/project.rb 等源码理解其背后的实现机制。

OpenProject 项目主页界面截图,展示项目概览页面

1. 什么是项目:定义、可见性与工作区类型

官方文档将项目定义为“一项有明确阶段、开始与结束日期、以达成既定目标为衡量标准的临时性目标驱动工作”。在 OpenProject 中,项目既可以按上述严格含义理解,也可以被用作团队的“工作区”(workspace),例如用来组织某个部门的协作。这一点在源码中得到了印证:Project 模型 通过 enum 定义了三种工作区类型:

enum :workspace_type, {
  project: "project",
  program: "program",
  portfolio: "portfolio"
}

即 OpenProject 的“项目”在实现层面统一为 project / program / portfolio 三种 workspace_type,分别对应项目、项目群(Program)与项目集(Portfolio)。文档中的提示“要看到项目并在其中工作,你必须成为项目成员”对应模型里的可见性判断:

def visible?(user = User.current)
  active? && (public? || user.admin? || user.access_to?(self))
end

也就是说:项目必须是激活状态(active?),且满足三者之一——项目公开(public?)、当前用户是系统管理员、或当前用户有权访问该项目(通常是成员身份)。这也解释了为什么新用户加入前看不到未公开的项目。

2. 打开一个已有项目

2.1 通过“全部项目”下拉菜单

打开项目最直接的入口是页面左上角页头导航中的 All projects 下拉菜单:

  1. 点击 All projects 下拉菜单,选择要打开的项目;
  2. 可以直接输入项目名按标题过滤,也可以切换到“收藏项目”过滤项,只显示你收藏过的项目;
  3. 项目和子项目(subproject)会按照层级结构在该下拉菜单中展示。

从源码结构看,这种层级展示由 Projects::Hierarchy 模块支撑:项目表使用嵌套集(nested set)模式维护 lft/rft 列(acts_as_nested_set order_column: :lft),build_projects_hierarchy 类方法按 lft 排序后构建出 { project:, children: } 形式的嵌套结构,并在每一层按名称排序(sort_by_name),随后 project_tree 以递归方式逐层产出项目及其深度——这正是下拉菜单中“父项目缩进展开子项目”的数据来源。

子项目的官方定义是:另一个项目的子项目,用于展示项目的层级关系;工作包表格、时间线等视图中的部分过滤选项可以“仅应用于当前项目及其子项目”。模型中还提供了 assignable_parents 等 scope 与 hierarchy 方法(父级链 + 所有后代),供权限与过滤逻辑复用。

2.2 其他打开项目的入口

  • 全局模块菜单(Global modules):左侧边栏的 Global modules 菜单中选择 Projects,可打开全部项目列表;
  • 应用落地页:最新项目和收藏项目会显示在应用落地页(landing page)的 Projects 区块,点击即可进入;你也可以在 All projects 下拉菜单中通过对应开关把项目设为收藏。

收藏功能在 Project 模型 中通过 acts_as_favoritable 实现,因此“收藏项目”是项目记录上的持久化状态,跨会话保留。

3. 创建新项目

创建新项目的权限与角色权限体系绑定(create_projects 权限),不同角色是否能看到创建入口取决于其被授予的权限。OpenProject 提供三种创建入口:

  1. 点击右上角页头导航中的 +(Plus) 按钮,选择“新建项目”;
  2. 在项目列表(project lists)概览页上创建;
  3. 如果要创建的是子项目,进入某个项目的“项目设置”,使用 + Subproject 按钮。

页头 + 按钮本身由 AddButtonComponent 组件族渲染(基础组件定义了 leading_icon:plus、要求子类实现目标路径与无障碍标签等),具体到“新建项目”则由相应子类实现其跳转路径与权限可见条件。

3.1 选择创建方式:空白、模板或项目启动申请

点击入口后,先选择项目的创建来源,可选:

  • 空白项目(Blank project):完全全新的空项目,默认选中此项;
  • 基于模板(based on a template):选择一个被标记为模板的已有项目;
  • 基于项目启动申请流程(project initiation request,Enterprise 附加组件):针对模板配置了启动申请流程时可用,项目创建后还会引导你走完预定义步骤。

为什么有时看不到模板选项? 文档给出的解释是:系统中还没有任何项目被设为项目模板,或者你对任何模板项目都没有访问权限——只有“公开模板”或“你是其成员的模板”才会出现在列表中,从而让不同用户组只看到与自身相关的模板。

这一行为在源码中有精确对应。Projects::Scopes::AvailableTemplates 定义了模板可见范围的完整逻辑:

def available_templates(workspace_type)
  allowed_to(User.current, :copy_projects)
     .active
     .templated
     .workspace_type(workspace_type)
end

即模板候选集 = 你拥有 copy_projects 权限的项目 ∩ 激活项目 ∩ 被标记 templated: true 的项目 ∩ 与当前创建工作区类型匹配的项目。Project 模型侧也维护了 belongs_to :template, class_name: "Project" 与反向的 has_many :templated_projects 关联,templated scope(where(templated: true))用于筛选。选择模板后点击 Continue 继续。

3.2 填写项目详情:名称、标识符与父项目

下一步输入项目名称(name)。OpenProject 会基于名称自动建议一个标识符(identifier),你也可以手动修改,系统会自动校验其合法性。文档说明了两条行为规则:

  • 标识符建议随项目名称的修改而自动更新;
  • 如果你手动编辑过标识符,之后再回到项目名称修改名称,OpenProject 会再次更新标识符建议。

名称本身的约束在 Project 模型 中定义:validates :name, presence: true, length: { maximum: 255 },并通过 normalizes :name 对首尾及多余空格做 squish 压缩——所以保存后的名称不会出现连续空格。

标识符的实现位于 Projects::Identifier 模块,值得逐条对照理解:

(1)两种格式与校验规则

格式正则最大长度说明
Classic(经典)/\A(?!\d+\z)[a-z0-9\-_]+\z/100(CLASSIC_IDENTIFIER_MAX_LENGTH小写字母、数字、连字符、下划线,且不能是纯数字
Semantic(语义)/[A-Z][A-Z0-9_]*/10(SEMANTIC_IDENTIFIER_MAX_LENGTH大写字母开头的短代码,如 PROJMY_PROJECT_1

经典格式的正则中 (?!\d+\z) 是“反全数字”守卫,确保标识符不是一段纯数字(避免与工作包编号混淆)。

(2)自动建议机制

# 仅在创建时且 identifier 为空、name 存在时触发
before_validation on: :create, if: -> { identifier.blank? && name.present? } do
  self.identifier = suggest_identifier
end

suggest_identifier 根据系统设置 Setting[:work_packages_identifier] 分派两种生成器:语义模式下由 ProjectIdentifierSuggestionGenerator 生成(并排除保留标识符),经典模式下由 ClassicIdentifierSuggestionGenerator 生成。两种模式下工作包编号的形态也不同:

  • 经典标识符:工作包使用全局编号,如 #123
  • 语义标识符:工作包使用“项目标识符 + 短横线 + 项目内序号”,如 PROJ1-123

(3)唯一性与历史保留

identifieruniqueness 校验是大小写不敏感的(case_sensitive: false)。此外模型启用了 FriendlyId 的 :history 能力(friendly_id :identifier, use: %i[finders history]):每次 identifier 变更都会在 project_identifiers(Slug 历史表)留痕,historically_reserved scope 专门筛出“已不再被任何激活项目使用、但仍被保留”的旧标识符——即旧标识符改名后不能立即被别的项目复用,这对依赖标识符的外部集成(API、链接)是一种稳定性保护。模块中还定义了保留词表:

RESERVED_IDENTIFIERS = %w[new menu queries filters
  identifier_update_dialog identifier_suggestion].freeze

这些词汇被路由或前端功能占用,不允许作为项目标识符。

(4)父项目与子项目

你还可以可选地填写项目描述(description),并通过选择**父项目(parent project)**把新项目挂入现有层级,使其成为子项目。父级选择并非任意:Project 模型 中用常量限制了每种工作区类型允许的父级类型:

ALLOWED_PARENT_WORKSPACE_TYPES = {
  project:   %i[portfolio program project],
  program:   %i[portfolio],
  portfolio: %i[]
}.with_indifferent_access

即 project 可以挂在 portfolio、program 或 project 之下,program 只能挂在 portfolio 之下,portfolio 没有父级。

文档补充了两条实战提示:

  • 从某个项目内部创建项目:若你在项目 A 内部发起创建(项目 B),则 B 默认成为 A 的子项目。此时表单中不显示“Subproject of”字段,父项目会显示在面包屑导航里;如果这不是你的本意,可以在项目 B 的项目设置中修改或移除父项目(模型通过 register_journal_formatted_fields "parent_id", formatter_key: :subproject_named_association 记录该变更,项目动态中会以可读形式展示父子关系调整);
  • 必填项目属性:若系统配置了必填的项目级自定义字段,创建流程中会插入额外步骤,必须填完这些属性才能完成创建。

3.3 项目启动申请(Enterprise 附加组件)

如果所用模板配置了项目启动申请(project initiation request),项目创建完成后你会被引导完成额外的预定义步骤。从源码看,该流程与“项目创建向导(creation wizard)”共用一套基础设施:Projects::CreationWizardController 按“区块(section)”加载项目自定义字段——只取映射中 creation_wizard: true 的字段(project_custom_field_project_mappings.where(creation_wizard: true)),并用 ProjectCustomFieldSection 把它们分组排序后逐节填写;最后一节提交时(last_page? 判断 params[:finish])会调用 SubmitArtifactService 生成“项目文档工件(artifact)”工作包并跳转过去。Projects::CreationWizard 模块 还定义了工件命名选项 ARTIFACT_NAME_OPTIONS = %w[project_creation_wizard project_initiation_request project_mandate],默认值为 project_creation_wizard,以及工作包类型、提交后状态、确认邮件等可配置项(均以 store_attribute 形式存于项目 settings JSON 中)。

3.4 新项目的成员如何确定

新建项目的成员构成取决于创建方式:

创建方式成员与角色来源
空白项目创建者自动成为成员,其项目角色由管理后台中的相应设置决定(即“创建项目时分配的角色”这一系统级配置)
基于模板继承模板中已定义的成员与角色(对应 Projecttemplate 关联,模板项目是模板源的“母版”)
从其他项目复制继承原项目的成员与角色;可通过项目信息页中的“复制项目”功能实现

文档特别提醒:执行复制的用户也会在新项目中被分配“创建项目用户的新角色(New role for users that create projects)”。根据配置,该角色可能授予比原项目中更多的权限——在给用户开放 copy_projects 权限时应留意这一点。模型侧 copy_allowed? 方法(检查 User.current.allowed_in_project?(:copy_projects, self))正是复制入口的权限闸门。

项目创建完成、配置好基础信息后,后续的深度配置(描述、公开性、项目层级、模块开关、自定义字段等)均在“项目设置”中完成。

4. 查看全部项目

要查看你作为成员的全部项目,有两种方式:

  1. 使用左侧 Global modules 菜单选择 Projects;
  2. 点击左上角的网格图标(grid icon),在下拉中选择 Projects 模块。

打开后可以看到所有项目的列表及其详情(名称、标识符、描述等)。项目概览列表背后的数据即 Project 在可见性 scope(visible)与成员 scope(with_member,取 user.memberships 中的 project_id)过滤下的集合。

5. 进阶:项目状态、归档与变更审计

虽然入门文档将高级设置指向了完整的项目设置指南,但有几个与日常使用强相关的源码级事实值得了解:

  • 项目状态(status_code):模型内置六值枚举 on_track / at_risk / off_track / not_started / finished / discontinued,用于在项目层面标记进展状态;
  • 归档与激活active 字段区分激活与归档项目,scope :activewhere(active: true))与 :archivedwhere(active: false))分别筛选;visible? 要求 active?,因此归档项目对普通用户不可见;
  • 完整变更审计:模型引入 has_paper_trail 并对 nameidentifierdescriptionpublicparent_id 等字段注册了带格式化器的 journal 字段(如 public:visibility 格式化、parent_id:subproject_named_association 格式化),因此“谁把项目改成了公开”“谁调整了父子关系”等项目级变更都可在项目动态中追溯。

6. 小结

操作入口关键规则(源码依据)
打开项目All projects 下拉 / Global modules / 落地页 Projects 区块可见性 = active? && (public? || admin || 有访问权)project.rb
层级展示All projects 下拉菜单嵌套集 lft/rft 构建层级树并按名称排序(hierarchy.rb
创建项目页头 + 按钮 / 项目列表 / + Subprojectcreate_projects 权限;模板候选受 copy_projects 权限 + templated 标记 + 工作区类型约束(available_templates.rb
标识符创建表单自动建议,可手改经典格式 ^[a-z0-9_-]+$ 且非纯数字(≤100),语义格式 ^[A-Z][A-Z0-9_]*$(≤10),不区分大小写唯一,旧值被历史保留(identifier.rb
父项目创建表单选择 / 项目设置修改仅允许 ALLOWED_PARENT_WORKSPACE_TYPES 定义的父子组合(project.rb
必填属性步骤创建流程中插入creation_wizard: true 的自定义字段映射驱动(creation_wizard_controller.rb

掌握以上内容,你即可完成 OpenProject 中“创建—命名—挂入层级—分配成员—查看管理”的完整项目工作流,并能对照源码准确回答诸如“为什么看不到模板”“标识符改名后能否复用”等实际部署中常见的问题。

【免费下载链接】openproject OpenProject 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 【免费下载链接】openproject 项目地址: https://gitcode.com/GitHub_Trending/op/openproject

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/gitblog_01094/article/details/157422044

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--