diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index a43038a..89a799a 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -194,6 +194,10 @@ class ProjectsController < ApplicationController @version_status = params[:version_status] || 'open' @version_name = params[:version_name] @versions = @project.shared_versions.status(@version_status).like(@version_name).sorted + @cfs=AttributeGroup.joins(:custom_fields).joins(:tracker). + where(project_id: @project, tracker_id: @trackers, :custom_fields => {id: @project.all_issue_custom_fields.pluck(:id)}). + pluck("trackers.id", "id", "name", "position","attribute_group_fields.id", "attribute_group_fields.position", + "custom_fields.id", "custom_fields.name", "custom_fields.position").sort_by{|x| [x[3], x[5]]} end def edit @@ -220,6 +224,39 @@ class ProjectsController < ApplicationController end end + def groupissuescustomfields + # clean invalid values: invalid cfs, empty cf lists, empty groups + group_issues_custom_fields = (JSON.parse params[:group_issues_custom_fields]). + each{|tid,v| v.replace(v.select{|k,v| v["cfs"] ? v["cfs"].delete_if{|k,v| @project.all_issue_custom_fields.pluck(:id).include?(v)} : v})}. + each{|tid,v| v.delete_if{|k,v| v["cfs"].blank?}}. + delete_if{|k,v| v.blank?} + + groups = AttributeGroup.where(project_id: @project.id).collect(&:id) + fields = AttributeGroupField.where(attribute_group_id: groups).collect(&:id) + group_issues_custom_fields.each do |tid,v| + v.each do |gp, g| + gid = groups.shift + if gid.nil? + gid=AttributeGroup.create(project_id: @project.id, tracker_id: tid, name: g["name"].nil? ? nil : g["name"], position: gp).id + else + AttributeGroup.update(gid, project_id: @project.id, tracker_id: tid, name: g["name"].nil? ? nil : g["name"], position: gp) + end + g['cfs'].each do |cfp, cf| + cfid = fields.shift + if cfid.nil? + AttributeGroupField.create(attribute_group_id: gid, custom_field_id: cf, position: cfp) + else + AttributeGroupField.update(cfid, attribute_group_id: gid, custom_field_id: cf, position: cfp) + end + end + end + end + AttributeGroupField.where(id: fields).delete_all + AttributeGroup.where(id: groups).destroy_all + flash[:notice] = l(:notice_successful_update) + redirect_to settings_project_path(@project, :tab => 'groupissuescustomfields') + end + def archive unless @project.archive flash[:error] = l(:error_can_not_archive_project) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index aee8444..25529a0 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -300,30 +300,45 @@ module IssuesHelper r.to_html end - def render_half_width_custom_fields_rows(issue) - values = issue.visible_custom_field_values.reject {|value| value.custom_field.full_width_layout?} - return if values.empty? - half = (values.size / 2.0).ceil - issue_fields_rows do |rows| - values.each_with_index do |value, i| - m = (i < half ? :left : :right) - rows.send m, custom_field_name_tag(value.custom_field), custom_field_value_tag(value), :class => value.custom_field.css_classes - end - end + def group_by_keys(project_id, tracker_id, custom_field_values) + keys_grouped = AttributeGroupField.joins(:attribute_group). + where(:attribute_groups => {project_id: project_id, tracker_id: tracker_id}). + order("attribute_groups.position", :position).pluck(:name, :custom_field_id).group_by(&:shift) + custom_fields_grouped = { nil => (keys_grouped[nil].nil? ? [] : keys_grouped[nil].map{|n| custom_field_values.select{|x| x.custom_field[:id] == n[0]}}.flatten) | + custom_field_values.select{|y| ! keys_grouped.values.flatten.include?(y.custom_field[:id])}} + keys_grouped.reject{|k,v| k == nil}.each{|k,v| custom_fields_grouped[k] = v.map{|n| custom_field_values.select{|x| x.custom_field[:id] == n[0]}}.flatten} + custom_fields_grouped end - def render_full_width_custom_fields_rows(issue) - values = issue.visible_custom_field_values.select {|value| value.custom_field.full_width_layout?} - return if values.empty? + def render_custom_fields_rows(issue) s = ''.html_safe - values.each_with_index do |value, i| - attr_value_tag = custom_field_value_tag(value) - next if attr_value_tag.blank? + group_by_keys(issue.project_id, issue.tracker_id, issue.visible_custom_field_values).each do |title, values| + if values.present? + s += content_tag('h4', title, :style => 'background: #0001; padding: 0.3em;') unless title.nil? + while values.present? + unless values[0].custom_field.full_width_layout? + lr_values = [] + while values.present? && ! values[0].custom_field.full_width_layout? + lr_values += [ values.shift ] + end + half = (lr_values.size / 2.0).ceil + s += issue_fields_rows do |rows| + lr_values.each_with_index do |value, i| + m = (i < half ? :left : :right) + rows.send m, custom_field_name_tag(value.custom_field), custom_field_value_tag(value), :class => value.custom_field.css_classes + end + end + else + while values.present? && values[0].custom_field.full_width_layout? + value=values.shift + content = content_tag('div', custom_field_name_tag(value.custom_field) + ":", :class => 'label') + + content_tag('div', custom_field_value_tag(value), :class => 'value') + content = content_tag('div', content, :class => "#{value.custom_field.css_classes} attribute") + s += content_tag('div', content, :class => 'splitcontent') + end + end + end + end - content = - content_tag('hr') + - content_tag('p', content_tag('strong', custom_field_name_tag(value.custom_field) )) + - content_tag('div', attr_value_tag, class: 'value') - s << content_tag('div', content, class: "#{value.custom_field.css_classes} attribute") end s end @@ -384,13 +399,15 @@ module IssuesHelper end end end - issue.visible_custom_field_values(user).each do |value| - cf_value = show_value(value, false) - next if cf_value.blank? - if html - items << content_tag('strong', "#{value.custom_field.name}: ") + cf_value - else - items << "#{value.custom_field.name}: #{cf_value}" + group_by_keys(issue.project_id, issue.tracker_id, issue.visible_custom_field_values(user)).each do |title, values| + if values.present? + item = [ (html ? content_tag('strong', "#{title}") : "#{title}") ] unless title.nil? + values.each do |value| + (title.nil? ? items : item) << (html ? + content_tag('strong', "#{value.custom_field.name}: ") + show_value(value, false) : + "#{value.custom_field.name}: #{show_value(value, false)}") + end + items << item unless title.nil? end end items @@ -399,9 +416,12 @@ module IssuesHelper def render_email_issue_attributes(issue, user, html=false) items = email_issue_attributes(issue, user, html) if html - content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe, :class => "details") + content_tag('ul', items.select{|s| s.is_a? String}.map{|s| content_tag('li', s)}.join("\n").html_safe, :class => "details") + "\n" + + items.select{|s| !s.is_a? String}.map{|item| content_tag('div', item.shift) + "\n" + + content_tag('ul', item.map{|s| content_tag('li', s)}.join("\n").html_safe, :class => "details")}.join("\n").html_safe else - items.map{|s| "* #{s}"}.join("\n") + items.select{|s| s.is_a? String}.map{|s| "* #{s}"}.join("\n") + "\n" + + items.select{|s| !s.is_a? String}.map{|item| "#{item.shift}\n" + item.map{|s| "* #{s}"}.join("\n")}.join("\n") end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 7945461..42124f1 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -39,7 +39,9 @@ module ProjectsHelper {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural}, {:name => 'activities', :action => :manage_project_activities, - :partial => 'projects/settings/activities', :label => :label_time_tracking} + :partial => 'projects/settings/activities', :label => :label_time_tracking}, + {:name => 'groupissuescustomfields', :action => :edit_project, + :partial => 'projects/settings/groupissuescustomfields', :label => :grouped_cf} ] tabs. select {|tab| User.current.allowed_to?(tab[:action], @project)}. diff --git a/app/models/attribute_group.rb b/app/models/attribute_group.rb new file mode 100644 index 0000000..52d7ac3 --- /dev/null +++ b/app/models/attribute_group.rb @@ -0,0 +1,9 @@ +class AttributeGroup < ActiveRecord::Base + belongs_to :project + belongs_to :tracker + has_many :attribute_group_fields, :dependent => :delete_all + has_many :custom_fields, :through => :attribute_group_fields + acts_as_positioned + + scope :sorted, lambda { order(:position) } +end diff --git a/app/models/attribute_group_field.rb b/app/models/attribute_group_field.rb new file mode 100644 index 0000000..c040138 --- /dev/null +++ b/app/models/attribute_group_field.rb @@ -0,0 +1,7 @@ +class AttributeGroupField < ActiveRecord::Base + belongs_to :attribute_group + belongs_to :custom_field + acts_as_positioned + + scope :sorted, lambda { order(:position) } +end diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 008ef49..41293a4 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -26,6 +26,7 @@ class CustomField < ActiveRecord::Base :class_name => 'CustomFieldEnumeration', :dependent => :delete_all has_many :custom_values, :dependent => :delete_all + has_many :attribute_group_fields, :dependent => :delete_all has_and_belongs_to_many :roles, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "custom_field_id" acts_as_positioned serialize :possible_values diff --git a/app/models/project.rb b/app/models/project.rb index 6d91d57..2888a9e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -53,6 +53,8 @@ class Project < ActiveRecord::Base has_many :changesets, :through => :repository has_one :wiki, :dependent => :destroy # Custom field for the project issues + has_many :attribute_groups, :dependent => :destroy + has_many :attribute_group_fields, :through => :attribute_groups has_and_belongs_to_many :issue_custom_fields, lambda {order(:position)}, :class_name => 'IssueCustomField', diff --git a/app/models/tracker.rb b/app/models/tracker.rb index d90ab3e..a4e9d79 100644 --- a/app/models/tracker.rb +++ b/app/models/tracker.rb @@ -32,6 +32,8 @@ class Tracker < ActiveRecord::Base has_many :workflow_rules, :dependent => :delete_all has_and_belongs_to_many :projects has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_trackers#{table_name_suffix}", :association_foreign_key => 'custom_field_id' + has_many :attribute_groups, :dependent => :destroy + has_many :attribute_group_fields, :through => :attribute_groups acts_as_positioned validates_presence_of :default_status diff --git a/app/views/issues/_form_custom_fields.html.erb b/app/views/issues/_form_custom_fields.html.erb index 13bedd5..f2991e8 100644 --- a/app/views/issues/_form_custom_fields.html.erb +++ b/app/views/issues/_form_custom_fields.html.erb @@ -1,24 +1,33 @@ -<% custom_field_values = @issue.editable_custom_field_values %> -<% custom_field_values_full_width = custom_field_values.select { |value| value.custom_field.full_width_layout? } %> -<% custom_field_values -= custom_field_values_full_width %> - -<% if custom_field_values.present? %> +<% group_by_keys(@issue.project_id, @issue.tracker_id, @issue.editable_custom_field_values).each do |title,values| %> +<% if values.present? %> +<%= content_tag('h4', title, :style => 'background: #0001; padding: 0.3em;') unless title.nil? %> +<% while values.present? %> +<% if values[0].custom_field.full_width_layout? %> +
<%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %>
+<%= wikitoolbar_for "issue_custom_field_values_#{value.custom_field_id}", preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id) if value.custom_field.full_text_formatting? %> +<% end %> +<% else %><%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %>
-<% if i == split_on -%> +<% split_on = (lr_values.size / 2.0).ceil - 1 %> +<% lr_values.each do |value| %> +<%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %>
+<% if i == split_on %><%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %>
- <%= wikitoolbar_for "issue_custom_field_values_#{value.custom_field_id}", preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id) if value.custom_field.full_text_formatting? %> +<% end %> <% end %> diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb index 5a1cc85..244fee2 100644 --- a/app/views/issues/show.html.erb +++ b/app/views/issues/show.html.erb @@ -71,7 +71,7 @@ rows.right l(:label_spent_time), issue_spent_hours_details(@issue), :class => 'spent-time' end end %> -<%= render_half_width_custom_fields_rows(@issue) %> +<%= render_custom_fields_rows(@issue) %> <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %><%= select_tag "tracker_id", options_from_collection_for_select(@project.trackers.collect, "id", "name"), {:required => true, :onchange => "refresh_trackers(this);"} %>
+ +
<%= submit_tag l(:button_save), :onclick => "fill_json_data();" %>
+<% end %> +