diff --git a/app/helpers/types_helper.rb b/app/helpers/types_helper.rb index c264e557ee..7b12f96eb2 100644 --- a/app/helpers/types_helper.rb +++ b/app/helpers/types_helper.rb @@ -62,7 +62,7 @@ module ::TypesHelper def work_package_form_attributes(merge_date: false) rattrs = API::V3::WorkPackages::Schema::WorkPackageSchemaRepresenter.representable_attrs definitions = rattrs[:definitions] - skip = ['_type', 'links', 'parent_id', 'parent', 'description'] + skip = ['_type', '_dependencies', 'attribute_groups', 'links', 'parent_id', 'parent', 'description'] attributes = definitions.keys .reject { |key| skip.include? key } .map { |key| [key, definitions[key]] }.to_h diff --git a/app/models/type.rb b/app/models/type.rb index c8471f7e29..707bae2730 100644 --- a/app/models/type.rb +++ b/app/models/type.rb @@ -58,6 +58,8 @@ class ::Type < ActiveRecord::Base end end + serialize :attribute_groups, Hash + acts_as_list validates_presence_of :name @@ -133,6 +135,41 @@ class ::Type < ActiveRecord::Base transition_exists?(status_id_a, status_id_b, roles.map(&:id)) end + def attribute_groups + groups = read_attribute :attribute_groups + + if groups.empty? + default_attribute_groups + else + groups + end + end + + def default_attribute_groups + values = attribute_visibility.keys.group_by { |key| map_attribute_to_group key } + + ordered = {} + + ordered["details"] = values["details"] + ordered["people"] = values["people"] + ordered["estimates_and_time"] = values["estimates_and_time"] + ordered["other"] = values["other"] + + ordered + end + + def map_attribute_to_group(name) + if ["author", "assignee", "reponsible"].include?(name) + "people" + elsif ["estimated_time", "spent_time"].include?(name) + "estimates_and_time" + elsif name =~ /custom/ + "other" + else + "details" + end + end + private def check_integrity diff --git a/db/migrate/20170222094032_add_attribute_groups_to_type.rb b/db/migrate/20170222094032_add_attribute_groups_to_type.rb new file mode 100644 index 0000000000..3e054b51fa --- /dev/null +++ b/db/migrate/20170222094032_add_attribute_groups_to_type.rb @@ -0,0 +1,5 @@ +class AddAttributeGroupsToType < ActiveRecord::Migration[5.0] + def change + add_column :types, :attribute_groups, :text, hash: true + end +end diff --git a/lib/api/decorators/allowed_values_by_collection_representer.rb b/lib/api/decorators/allowed_values_by_collection_representer.rb index f37314a7e7..10aa95b036 100644 --- a/lib/api/decorators/allowed_values_by_collection_representer.rb +++ b/lib/api/decorators/allowed_values_by_collection_representer.rb @@ -44,6 +44,7 @@ module API has_default: false, writable: true, visibility: nil, + attribute_group: nil, current_user: nil) @value_representer = value_representer @link_factory = link_factory @@ -54,6 +55,7 @@ module API has_default: has_default, writable: writable, visibility: visibility, + attribute_group: attribute_group, current_user: current_user) end diff --git a/lib/api/decorators/property_schema_representer.rb b/lib/api/decorators/property_schema_representer.rb index 84f953e1bf..64b14a0f32 100644 --- a/lib/api/decorators/property_schema_representer.rb +++ b/lib/api/decorators/property_schema_representer.rb @@ -35,7 +35,7 @@ module API class PropertySchemaRepresenter < ::API::Decorators::Single def initialize( type:, name:, required: true, has_default: false, writable: true, - visibility: nil, current_user: nil + visibility: nil, attribute_group: nil, current_user: nil ) @type = type @name = name @@ -47,6 +47,7 @@ module API else visibility || 'default' end + @attribute_group = attribute_group || 'other' super(nil, current_user: current_user) end @@ -57,6 +58,7 @@ module API :has_default, :writable, :visibility, + :attribute_group, :min_length, :max_length, :regular_expression @@ -67,6 +69,7 @@ module API property :has_default, exec_context: :decorator property :writable, exec_context: :decorator property :visibility, exec_context: :decorator + property :attribute_group, exec_context: :decorator property :min_length, exec_context: :decorator property :max_length, exec_context: :decorator property :regular_expression, exec_context: :decorator diff --git a/lib/api/decorators/schema_representer.rb b/lib/api/decorators/schema_representer.rb index 91a0dcd072..55a2f119f4 100644 --- a/lib/api/decorators/schema_representer.rb +++ b/lib/api/decorators/schema_representer.rb @@ -58,6 +58,7 @@ module API has_default: false, writable: default_writable_property(property), visibility: nil, + attribute_group: nil, min_length: nil, max_length: nil, regular_expression: nil, @@ -69,6 +70,7 @@ module API has_default, writable, visibility, + attribute_group, min_length, max_length, regular_expression) @@ -90,6 +92,7 @@ module API has_default: false, writable: default_writable_property(property), visibility: nil, + attribute_group: nil, show_if: true) getter = ->(*) do schema_with_allowed_link_property_getter(type, @@ -98,6 +101,7 @@ module API has_default, writable, visibility, + attribute_group, href_callback) end @@ -121,6 +125,7 @@ module API has_default: false, writable: default_writable_property(property), visibility: nil, + attribute_group: nil, show_if: true) getter = ->(*) do @@ -133,6 +138,7 @@ module API has_default, writable, visibility, + attribute_group, values_callback) end @@ -229,6 +235,7 @@ module API has_default, writable, visibility, + attribute_group, min_length, max_length, regular_expression) @@ -239,7 +246,8 @@ module API required: call_or_use(required), has_default: call_or_use(has_default), writable: call_or_use(writable), - visibility: call_or_use(visibility)) + visibility: call_or_use(visibility), + attribute_group: call_or_use(attribute_group)) schema.min_length = min_length schema.max_length = max_length schema.regular_expression = regular_expression @@ -253,6 +261,7 @@ module API has_default, writable, visibility, + attribute_group, href_callback) representer = ::API::Decorators::AllowedValuesByLinkRepresenter .new(type: call_or_use(type), @@ -260,7 +269,8 @@ module API required: call_or_use(required), has_default: call_or_use(has_default), writable: call_or_use(writable), - visibility: call_or_use(visibility)) + visibility: call_or_use(visibility), + attribute_group: call_or_use(attribute_group)) if form_embedded representer.allowed_values_href = instance_eval(&href_callback) @@ -278,6 +288,7 @@ module API has_default, writable, visibility, + attribute_group, values_callback) representer = ::API::Decorators::AllowedValuesByCollectionRepresenter .new(type: call_or_use(type), @@ -288,7 +299,8 @@ module API required: call_or_use(required), has_default: call_or_use(has_default), writable: call_or_use(writable), - visibility: call_or_use(visibility)) + visibility: call_or_use(visibility), + attribute_group: call_or_use(attribute_group)) if form_embedded representer.allowed_values = instance_exec(&values_callback) diff --git a/lib/api/v3/work_packages/schema/base_work_package_schema.rb b/lib/api/v3/work_packages/schema/base_work_package_schema.rb index 1ad56d51bc..373998c2ca 100644 --- a/lib/api/v3/work_packages/schema/base_work_package_schema.rb +++ b/lib/api/v3/work_packages/schema/base_work_package_schema.rb @@ -94,6 +94,14 @@ module API false end + def attribute_groups + groups = type.attribute_groups.presence || type.default_attribute_groups + + groups.each_with_index.map do |(name, values), i| + { name: name, position: i, values: values } + end + end + private def percentage_done_writable? diff --git a/lib/api/v3/work_packages/schema/work_package_schema_representer.rb b/lib/api/v3/work_packages/schema/work_package_schema_representer.rb index 717d768ba4..2c59b472c4 100644 --- a/lib/api/v3/work_packages/schema/work_package_schema_representer.rb +++ b/lib/api/v3/work_packages/schema/work_package_schema_representer.rb @@ -60,12 +60,24 @@ module API end end + def attribute_group(property) + lambda do + if type = represented.type + key = property.to_s.gsub /^customField/, "custom_field_" + group = type.attribute_groups.find { |name, values| values.include?(key) }.try(:first) + + group || type.attribute_groups.keys.first + end + end + end + # override the various schema methods to include # the same visibility lambda for all properties by default def schema(property, *args) opts, _ = args opts[:visibility] = visibility property + opts[:attribute_group] = attribute_group property super property, **opts end @@ -73,6 +85,7 @@ module API def schema_with_allowed_link(property, *args) opts, _ = args opts[:visibility] = visibility property + opts[:attribute_group] = attribute_group property super property, **opts end @@ -80,6 +93,7 @@ module API def schema_with_allowed_collection(property, *args) opts, _ = args opts[:visibility] = visibility property + opts[:attribute_group] = attribute_group property super property, **opts end @@ -107,6 +121,10 @@ module API { href: @base_schema_link } if @base_schema_link end + property :attribute_groups, + type: "[]String", + as: "_attributeGroups" + schema :lock_version, type: 'Integer', name_source: -> (*) { I18n.t('api_v3.attributes.lock_version') },