Skip to content

Commit

Permalink
[57812] Added model and migration for custom field of type hierarchy (#…
Browse files Browse the repository at this point in the history
…16805)

* Added model and migration for custom field of type hierarchy
* Added feature flag for custom field of type hierarchy
* moved feature flag to drop down options

---------

Co-authored-by: Eric Schubert <[email protected]>
  • Loading branch information
apfohl and Kharonus authored Sep 26, 2024
1 parent c787d59 commit 4e80bd3
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 8 deletions.
5 changes: 5 additions & 0 deletions app/helpers/custom_fields_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,15 @@ def format_value(value, custom_field)

# Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select(custom_field)
hierarchy_if_deactivated = lambda do |format|
format.name == "hierarchy" && !OpenProject::FeatureDecisions.custom_field_of_type_hierarchy_active?
end

OpenProject::CustomFieldFormat
.all_for_field(custom_field)
.sort_by(&:order)
.reject { |format| format.label.nil? }
.reject(&hierarchy_if_deactivated)
.map do |custom_field_format|
[label_for_custom_field_format(custom_field_format.name), custom_field_format.name]
end
Expand Down
5 changes: 5 additions & 0 deletions app/models/custom_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class CustomField < ApplicationRecord
inverse_of: "custom_field"
accepts_nested_attributes_for :custom_options

has_one :hierarchy_root,
class_name: "CustomField::Hierarchy::Item",
dependent: :delete, # todo: cascade into children with service
inverse_of: "custom_field"

acts_as_list scope: [:type]

validates :field_format, presence: true
Expand Down
36 changes: 36 additions & 0 deletions app/models/custom_field/hierarchy/item.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class CustomField::Hierarchy::Item < ApplicationRecord
self.table_name = "hierarchical_items"

belongs_to :custom_field
has_closure_tree order: "sort_order", numeric_order: true
end
52 changes: 52 additions & 0 deletions app/models/custom_value/hierarchy_strategy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class CustomValue::HierarchyStrategy < CustomValue::ARObjectStrategy
def validate_type_of_value
raise NotImplementedError
end

def typed_value
raise NotImplementedError
end

private

def ar_class
CustomField::Hierarchy::Item
end

def ar_object(value)
option = CustomField::Hierarchy::Item.find_by(id: value.to_s)
if option.nil?
"#{value} #{I18n.t(:label_not_found)}"
else
option.value
end
end
end
11 changes: 7 additions & 4 deletions config/initializers/custom_field_format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,13 @@
formatter: "CustomValue::BoolStrategy")
fields.register OpenProject::CustomFieldFormat.new("user",
label: Proc.new { User.model_name.human },
only: %w(WorkPackage TimeEntry
Version Project),
only: %w(WorkPackage TimeEntry Version Project),
edit_as: "list",
order: 9,
formatter: "CustomValue::UserStrategy")
fields.register OpenProject::CustomFieldFormat.new("version",
label: Proc.new { Version.model_name.human },
only: %w(WorkPackage TimeEntry
Version Project),
only: %w(WorkPackage TimeEntry Version Project),
edit_as: "list",
order: 10,
formatter: "CustomValue::VersionStrategy")
Expand All @@ -79,4 +77,9 @@
label: nil,
order: 11,
formatter: "CustomValue::EmptyStrategy")

fields.register OpenProject::CustomFieldFormat.new("hierarchy",
label: :label_hierarchy,
order: 12,
formatter: "CustomValue::HierarchyStrategy")
end
3 changes: 3 additions & 0 deletions config/initializers/feature_decisions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@
# end
OpenProject::FeatureDecisions.add :built_in_oauth_applications,
description: "Allows the display and use of built-in OAuth applications."

OpenProject::FeatureDecisions.add :custom_field_of_type_hierarchy,
description: "Allows the use of the custom field type 'Hierarchy'."
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2268,6 +2268,7 @@ en:
label_here: here
label_hide: "Hide"
label_history: "History"
label_hierarchy: "Hierarchy"
label_hierarchy_leaf: "Hierarchy leaf"
label_home: "Home"
label_subject_or_id: "Subject or ID"
Expand Down
29 changes: 29 additions & 0 deletions db/migrate/20240924114246_create_hierarchical_items.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class CreateHierarchicalItems < ActiveRecord::Migration[7.1]
def change
create_table :hierarchical_items do |t|
t.integer :parent_id
t.integer :sort_order
t.string :label
t.string :short
t.boolean :is_deleted, default: false, null: false

t.timestamps
end

add_reference "hierarchical_items", :custom_field, foreign_key: true

# auto-generated by closure_tree
create_table :hierarchical_item_hierarchies, id: false do |t|
t.integer :ancestor_id, null: false
t.integer :descendant_id, null: false
t.integer :generations, null: false
end

add_index :hierarchical_item_hierarchies, %i[ancestor_id descendant_id generations],
unique: true,
name: "item_anc_desc_idx"

add_index :hierarchical_item_hierarchies, [:descendant_id],
name: "item_desc_idx"
end
end
11 changes: 7 additions & 4 deletions lib/api/v3/utilities/custom_field_injector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,24 @@ class CustomFieldInjector
"bool" => "Boolean",
"user" => "User",
"version" => "Version",
"list" => "CustomOption"
"list" => "CustomOption",
"hierarchy" => "CustomField::Hierarchy::Item"
}.freeze

LINK_FORMATS = %w(list user version).freeze
LINK_FORMATS = %w(list user version hierarchy).freeze

NAMESPACE_MAP = {
"user" => ["users", "groups", "placeholder_users"],
"version" => "versions",
"list" => "custom_options"
"list" => "custom_options",
"hierarchy" => "hierarchical_items"
}.freeze

REPRESENTER_MAP = {
"user" => "::API::V3::Principals::PrincipalRepresenterFactory",
"version" => "::API::V3::Versions::VersionRepresenter",
"list" => "::API::V3::CustomOptions::CustomOptionRepresenter"
"list" => "::API::V3::CustomOptions::CustomOptionRepresenter",
"hierarchy" => "::API::V3::HierarchyItems::HierarchyItemRepresenter"
}.freeze

class << self
Expand Down

0 comments on commit 4e80bd3

Please sign in to comment.