diff --git app/controllers/issues_controller.rb app/controllers/issues_controller.rb index 8a8162b..bd648d1 100644 --- app/controllers/issues_controller.rb +++ app/controllers/issues_controller.rb @@ -118,6 +118,7 @@ class IssuesController < ApplicationController @edit_allowed = User.current.allowed_to?(:edit_issues, @project) @priorities = IssuePriority.all @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) + @custom_values = @issue.custom_field_values respond_to do |format| format.html { render :template => 'issues/show.rhtml' } format.api @@ -285,6 +286,7 @@ private @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) @time_entry.attributes = params[:time_entry] + @custom_values = @issue.custom_field_values @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil) @issue.init_journal(User.current, @notes) @issue.safe_attributes = params[:issue] diff --git app/helpers/custom_fields_helper.rb app/helpers/custom_fields_helper.rb index 3028cd4..fdccf06 100644 --- app/helpers/custom_fields_helper.rb +++ app/helpers/custom_fields_helper.rb @@ -49,7 +49,12 @@ module CustomFieldsHelper blank_option = custom_field.is_required? ? (custom_field.default_value.blank? ? "" : '') : '' - select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id) + multi_image = custom_field.allow_multi ? + link_to_function(image_tag('bullet_toggle_plus.png'), "toggle_multi_custom('#{custom_field.id}');", :style => "vertical-align: bottom;") : + '' + multiple = custom_field.allow_multi && custom_value.value.is_a?(Array) && custom_value.value.length > 1 + select_name = custom_field.allow_multi ? "#{name}[custom_multi_values][#{custom_field.id}][]" : field_name + select_tag(select_name, blank_option + options_for_select(custom_field.possible_values, custom_value.value), :id => field_id, :multiple => multiple) + multi_image else text_field_tag(field_name, custom_value.value, :id => field_id) end @@ -92,7 +97,7 @@ module CustomFieldsHelper # Return a string used to display a custom value def show_value(custom_value) return "" unless custom_value - format_value(custom_value.value, custom_value.custom_field.field_format) + format_value((custom_value.value.is_a?(Array) ? custom_value.value.join("\n") : custom_value.value), custom_value.custom_field.field_format) end # Return a string used to display a custom value diff --git app/models/custom_field.rb app/models/custom_field.rb index 0e8151e..b13028b 100644 --- app/models/custom_field.rb +++ app/models/custom_field.rb @@ -33,6 +33,8 @@ class CustomField < ActiveRecord::Base def before_validation # make sure these fields are not searchable self.searchable = false if %w(int float date bool).include?(field_format) + # make sure only list field_format have allow_multi option + self.allow_multi = false unless field_format == 'list' true end diff --git app/models/custom_value.rb app/models/custom_value.rb index 97bd47f..e11a437 100644 --- app/models/custom_value.rb +++ app/models/custom_value.rb @@ -69,3 +69,48 @@ protected end end end + +class CustomValuesCollection < Array + attr_accessor :custom_field + + def initialize(custom_field, custom_values=[]) + @custom_field = custom_field if custom_field.is_a?(CustomField) + custom_values.map{ |x| self << x } + self + end + + def value + self.uniq.map(&:value).delete_if {|x| x.blank?} + end + + def value=(new_value) + self.delete_if{ |x| true } + new_value.map{ |x| self << x } + end + + def save + self.compact.each(&:save) + end + + def valid? + self.inject(true){ |bool,v| bool && v.valid? } + end + + def validate + self.uniq.map(&:validate) + end + + def custom_field_id + @custom_field.id + end + + def method_missing(symbol, *args) + if @custom_field.respond_to?(symbol) + @custom_field.send(symbol, *args) + elsif self.first && self.first.respond_to?(symbol) + self.first.send(symbol, *args) + else + super + end + end +end diff --git app/models/issue.rb app/models/issue.rb index 79c4915..2ad0d86 100644 --- app/models/issue.rb +++ app/models/issue.rb @@ -264,6 +264,7 @@ class Issue < ActiveRecord::Base 'due_date', 'done_ratio', 'estimated_hours', + 'custom_multi_values', 'custom_field_values', 'custom_fields', 'lock_version', @@ -393,7 +394,7 @@ class Issue < ActiveRecord::Base @issue_before_change = self.clone @issue_before_change.status = self.status @custom_values_before_change = {} - self.custom_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value } + custom_field_values.each {|c| @custom_values_before_change.store c.custom_field.id, c.value } # Make sure updated_on is updated when adding a note. updated_on_will_change! @current_journal @@ -886,7 +887,7 @@ class Issue < ActiveRecord::Base :value => send(c)) } # custom fields changes - custom_values.each {|c| + custom_field_values.each {|c| next if (@custom_values_before_change[c.custom_field_id]==c.value || (@custom_values_before_change[c.custom_field_id].blank? && c.value.blank?)) @current_journal.details << JournalDetail.new(:property => 'cf', diff --git app/models/journal_detail.rb app/models/journal_detail.rb index 886fe3e..aaa5dcb 100644 --- app/models/journal_detail.rb +++ app/models/journal_detail.rb @@ -22,6 +22,8 @@ class JournalDetail < ActiveRecord::Base private def normalize_values + self.value = value.join(", ") if value && value.is_a?(Array) + self.old_value = old_value.join(", ") if old_value && old_value.is_a?(Array) self.value = normalize(value) self.old_value = normalize(old_value) end diff --git app/views/custom_fields/_form.rhtml app/views/custom_fields/_form.rhtml index 2900af9..f05abf9 100644 --- app/views/custom_fields/_form.rhtml +++ app/views/custom_fields/_form.rhtml @@ -5,6 +5,7 @@ function toggle_custom_field_format() { format = $("custom_field_field_format"); p_length = $("custom_field_min_length"); + p_multi = $("custom_field_allow_multi"); p_regexp = $("custom_field_regexp"); p_values = $("custom_field_possible_values"); p_searchable = $("custom_field_searchable"); @@ -19,6 +20,7 @@ function toggle_custom_field_format() { Element.hide(p_regexp.parentNode); if (p_searchable) Element.show(p_searchable.parentNode); Element.show(p_values.parentNode); + Element.show(p_multi.parentNode); break; case "bool": p_default.setAttribute('type','checkbox'); @@ -26,12 +28,14 @@ function toggle_custom_field_format() { Element.hide(p_regexp.parentNode); if (p_searchable) Element.hide(p_searchable.parentNode); Element.hide(p_values.parentNode); + Element.hide(p_multi.parentNode); break; case "date": Element.hide(p_length.parentNode); Element.hide(p_regexp.parentNode); if (p_searchable) Element.hide(p_searchable.parentNode); Element.hide(p_values.parentNode); + Element.hide(p_multi.parentNode); break; case "float": case "int": @@ -39,6 +43,7 @@ function toggle_custom_field_format() { Element.show(p_regexp.parentNode); if (p_searchable) Element.hide(p_searchable.parentNode); Element.hide(p_values.parentNode); + Element.hide(p_multi.parentNode); break; case "user": case "version": @@ -53,6 +58,7 @@ function toggle_custom_field_format() { Element.show(p_regexp.parentNode); if (p_searchable) Element.show(p_searchable.parentNode); Element.hide(p_values.parentNode); + Element.hide(p_multi.parentNode); break; } } @@ -91,6 +97,7 @@ when "IssueCustomField" %>
<%= f.check_box :is_for_all %>
<%= f.check_box :is_filter %>
<%= f.check_box :searchable %>
+<%= f.check_box :allow_multi %>
<% when "UserCustomField" %><%= f.check_box :is_required %>
diff --git app/views/issues/_form_custom_fields.rhtml app/views/issues/_form_custom_fields.rhtml index 7a66ed3..549a078 100644 --- app/views/issues/_form_custom_fields.rhtml +++ app/views/issues/_form_custom_fields.rhtml @@ -1,3 +1,15 @@ +