]> source.dussan.org Git - redmine.git/commitdiff
Ability to disable standard fields on a per tracker basis (#1091).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Thu, 5 Jul 2012 12:20:07 +0000 (12:20 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Thu, 5 Jul 2012 12:20:07 +0000 (12:20 +0000)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9912 e93f8b46-1217-0410-a6f0-8f06a7374b81

18 files changed:
app/controllers/context_menus_controller.rb
app/helpers/issues_helper.rb
app/helpers/versions_helper.rb
app/models/issue.rb
app/models/query.rb
app/models/tracker.rb
app/views/context_menus/issues.html.erb
app/views/issues/_attributes.html.erb
app/views/issues/bulk_edit.html.erb
app/views/issues/show.html.erb
app/views/trackers/_form.html.erb
config/locales/en.yml
config/locales/fr.yml
db/migrate/20120705074331_add_trackers_fields_bits.rb [new file with mode: 0644]
lib/redmine/export/pdf.rb
test/functional/trackers_controller_test.rb
test/unit/issue_test.rb
test/unit/tracker_test.rb

index 36041fec6e3ef19bffd297b572c232b6f568d481..f371b2e042c42f7aef1266a126c2124f37d2be06 100644 (file)
@@ -65,6 +65,7 @@ class ContextMenusController < ApplicationController
       end
     end
 
+    @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
     render :layout => false
   end
 
index d35ada049881f23952e8bb9f11041a04b698e527..ece3f78447014aab0b27412ebcc5e452265a860b 100644 (file)
@@ -92,6 +92,48 @@ module IssuesHelper
     s.html_safe
   end
 
+  class IssueFieldsRows
+    include ActionView::Helpers::TagHelper
+
+    def initialize
+      @left = []
+      @right = []
+    end
+
+    def left(*args)
+      args.any? ? @left << cells(*args) : @left
+    end
+
+    def right(*args)
+      args.any? ? @right << cells(*args) : @right
+    end
+
+    def size
+      @left.size > @right.size ? @left.size : @right.size
+    end
+
+    def to_html
+      html = ''.html_safe
+      blank = content_tag('th', '') + content_tag('td', '')
+      size.times do |i|
+        left = @left[i] || blank
+        right = @right[i] || blank
+        html << content_tag('tr', left + right)
+      end
+      html
+    end
+
+    def cells(label, text, options={})
+      content_tag('th', "#{label}:", options) + content_tag('td', text, options)
+    end
+  end
+
+  def issue_fields_rows
+    r = IssueFieldsRows.new
+    yield r
+    r.to_html
+  end
+
   def render_custom_fields_rows(issue)
     return if issue.custom_field_values.empty?
     ordered_values = []
index f1fd7c8b52489d173069a2525b5e725581914b5f..e6f8867342143e796f52db583539d247c6517089 100644 (file)
 
 module VersionsHelper
 
-  STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
+  STATUS_BY_CRITERIAS = %w(tracker status priority author assigned_to category)
 
   def render_issue_status_by(version, criteria)
-    criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
+    criteria = 'tracker' unless STATUS_BY_CRITERIAS.include?(criteria)
 
     h = Hash.new {|k,v| k[v] = [0, 0]}
     begin
index 96c3f8526630188b1876fa887e2f3a02473ac703..5fc0f1d615f5b2bbaf5e5aa9eb812f3ed597e61e 100644 (file)
@@ -336,6 +336,12 @@ class Issue < ActiveRecord::Base
     :if => lambda {|issue, user| (issue.new_record? || user.allowed_to?(:edit_issues, issue.project)) &&
       user.allowed_to?(:manage_subtasks, issue.project)}
 
+  def safe_attribute_names(*args)
+    names = super(*args)
+    names -= disabled_core_fields
+    names
+  end
+
   # Safely sets attributes
   # Should be called from controllers instead of #attributes=
   # attr_accessible is too rough because we still want things like
@@ -343,21 +349,22 @@ class Issue < ActiveRecord::Base
   def safe_attributes=(attrs, user=User.current)
     return unless attrs.is_a?(Hash)
 
-    # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
-    attrs = delete_unsafe_attributes(attrs, user)
-    return if attrs.empty?
+    attrs = attrs.dup
 
     # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
-    if p = attrs.delete('project_id')
+    if (p = attrs.delete('project_id')) && safe_attribute?('project_id')
       if allowed_target_projects(user).collect(&:id).include?(p.to_i)
         self.project_id = p
       end
     end
 
-    if t = attrs.delete('tracker_id')
+    if (t = attrs.delete('tracker_id')) && safe_attribute?('tracker_id')
       self.tracker_id = t
     end
 
+    attrs = delete_unsafe_attributes(attrs, user)
+    return if attrs.empty?
+
     if attrs['status_id']
       unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i)
         attrs.delete('status_id')
@@ -376,6 +383,10 @@ class Issue < ActiveRecord::Base
     assign_attributes attrs, :without_protection => true
   end
 
+  def disabled_core_fields
+    tracker ? tracker.disabled_core_fields : []
+  end
+
   def done_ratio
     if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
       status.default_done_ratio
index de5fa4c16e0c0ccbd5bfc763285fd857deb71dad..3f4173a1b6a4a91548bb268ff68810e9c75be5d1 100644 (file)
@@ -213,11 +213,13 @@ class Query < ActiveRecord::Base
     is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
   end
 
+  def trackers
+    @trackers ||= project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers
+  end
+
   def available_filters
     return @available_filters if @available_filters
 
-    trackers = project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers
-
     @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
                            "tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },
                            "priority_id" => { :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } },
@@ -300,6 +302,11 @@ class Query < ActiveRecord::Base
       end
       add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true}))
     end
+    
+    Tracker.disabled_core_fields(trackers).each {|field|
+      @available_filters.delete field
+    }
+
     @available_filters
   end
 
@@ -380,6 +387,12 @@ class Query < ActiveRecord::Base
         :caption => :label_spent_time
       )
     end
+
+    disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
+    @available_columns.reject! {|column|
+      disabled_fields.include?(column.name.to_s)
+    }
+
     @available_columns
   end
 
index ce34f782cd93ccaab22c69abb1cce63a55f3d716..109e0f42393f803016c11581be4f00f18867eaa3 100644 (file)
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class Tracker < ActiveRecord::Base
+
+  # Other fields should be appended, not inserted!
+  CORE_FIELDS = %w(assigned_to_id category_id fixed_version_id parent_issue_id start_date due_date estimated_hours done_ratio)
+
   before_destroy :check_integrity
   has_many :issues
   has_many :workflows, :dependent => :delete_all do
@@ -28,6 +32,8 @@ class Tracker < ActiveRecord::Base
   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'
   acts_as_list
 
+  attr_protected :field_bits
+
   validates_presence_of :name
   validates_uniqueness_of :name
   validates_length_of :name, :maximum => 30
@@ -58,6 +64,39 @@ class Tracker < ActiveRecord::Base
     @issue_statuses = IssueStatus.find_all_by_id(ids).sort
   end
 
+  def disabled_core_fields
+    i = -1
+    @disabled_core_fields ||= CORE_FIELDS.select { i += 1; (fields_bits || 0) & (2 ** i) != 0}
+  end
+
+  def core_fields
+    CORE_FIELDS - disabled_core_fields
+  end
+
+  def core_fields=(fields)
+    raise ArgumentError.new("Tracker.core_fields takes an array") unless fields.is_a?(Array)
+
+    bits = 0
+    CORE_FIELDS.each_with_index do |field, i|
+      unless fields.include?(field)
+        bits |= 2 ** i
+      end
+    end
+    self.fields_bits = bits
+    @disabled_core_fields = nil
+    core_fields
+  end
+
+  # Returns the fields that are disabled for all the given trackers
+  def self.disabled_core_fields(trackers)
+    trackers.uniq.map(&:disabled_core_fields).reduce(:&)
+  end
+
+  # Returns the fields that are enabled for one tracker at least
+  def self.core_fields(trackers)
+    trackers.uniq.map(&:core_fields).reduce(:|)
+  end
+
 private
   def check_integrity
     raise Exception.new("Can't delete tracker") if Issue.where(:tracker_id => self.id).any?
index e2b87944cfeb35c9424d599eed7877e4a1bede1b..75b0a7652bd50efa03a8828420088c2226e035de 100644 (file)
@@ -33,6 +33,7 @@
   </li>
   <% end %>
 
+  <% if @safe_attributes.include?('priority_id') -%>
   <li class="folder">
     <a href="#" class="submenu"><%= l(:field_priority) %></a>
     <ul>
     <% end -%>
     </ul>
   </li>
+  <% end %>
 
   <% #TODO: allow editing versions when multiple projects %>
-  <% unless @project.nil? || @project.shared_versions.open.empty? -%>
+  <% if @safe_attributes.include?('fixed_version_id') && @project && @project.shared_versions.open.any? -%>
   <li class="folder">
     <a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
     <ul>
@@ -57,7 +59,8 @@
     </ul>
   </li>
   <% end %>
-  <% if @assignables.present? -%>
+
+  <% if @safe_attributes.include?('assigned_to_id') && @assignables.present? -%>
   <li class="folder">
     <a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
     <ul>
@@ -74,7 +77,8 @@
     </ul>
   </li>
   <% end %>
-  <% unless @project.nil? || @project.issue_categories.empty? -%>
+
+  <% if @safe_attributes.include?('category_id') && @project && @project.issue_categories.any? -%>
   <li class="folder">
     <a href="#" class="submenu"><%= l(:field_category) %></a>
     <ul>
@@ -88,7 +92,7 @@
   </li>
   <% end -%>
 
-  <% if Issue.use_field_for_done_ratio? %>
+  <% if @safe_attributes.include?('done_ratio') && Issue.use_field_for_done_ratio? %>
   <li class="folder">
     <a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
     <ul>
index 0bec5316334944ebe74c95a5f151adc820529df4..d2c13e82be9575ed59a47de7b5815fcf160e7032 100644 (file)
@@ -64,3 +64,5 @@
 <% end %>
 
 <% end %>
+
+<% include_calendar_headers_tags %>
index 946b5255bfde498798ee3cf4002f45face00a7be..63bc1f7df2d489ca325870894421255739bfb8b4 100644 (file)
   <%= select_tag('issue[status_id]',content_tag('option', l(:label_no_change_option), :value => '') + options_from_collection_for_select(@available_statuses, :id, :name)) %>
 </p>
 <% end %>
+
+<% if @safe_attributes.include?('priority_id') -%>
 <p>
   <label for='issue_priority_id'><%= l(:field_priority) %></label>
   <%= select_tag('issue[priority_id]', content_tag('option', l(:label_no_change_option), :value => '') + options_from_collection_for_select(IssuePriority.active, :id, :name)) %>
 </p>
+<% end %>
+
+<% if @safe_attributes.include?('assigned_to_id') -%>
 <p>
   <label for='issue_assigned_to_id'><%= l(:field_assigned_to) %></label>
   <%= select_tag('issue[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
                                  content_tag('option', l(:label_nobody), :value => 'none') +
                                  principals_options_for_select(@assignables)) %>
 </p>
+<% end %>
+
+<% if @safe_attributes.include?('category_id') -%>
 <p>
   <label for='issue_category_id'><%= l(:field_category) %></label>
   <%= select_tag('issue[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
                                 content_tag('option', l(:label_none), :value => 'none') +
                                 options_from_collection_for_select(@categories, :id, :name)) %>
 </p>
+<% end %>
+
+<% if @safe_attributes.include?('fixed_version_id') -%>
 <p>
   <label for='issue_fixed_version_id'><%= l(:field_fixed_version) %></label>
   <%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
                                    content_tag('option', l(:label_none), :value => 'none') +
                                    version_options_for_select(@versions.sort)) %>
 </p>
+<% end %>
 
 <% @custom_fields.each do |custom_field| %>
   <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('issue', custom_field, @projects) %></p>
@@ -79,7 +91,8 @@
                                 content_tag('option', l(:general_text_No), :value => '0')) %>
 </p>
 <% end %>
-<% if @project && User.current.allowed_to?(:manage_subtasks, @project) %>
+
+<% if @safe_attributes.include?('parent_issue_id') && @project %>
 <p>
   <label for='issue_parent_issue_id'><%= l(:field_parent_issue) %></label>
   <%= text_field_tag 'issue[parent_issue_id]', '', :size => 10 %>
 <div id="parent_issue_candidates" class="autocomplete"></div>
 <%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:project_id => @project) }')" %>
 <% end %>
+
+<% if @safe_attributes.include?('start_date') %>
 <p>
   <label for='issue_start_date'><%= l(:field_start_date) %></label>
   <%= text_field_tag 'issue[start_date]', '', :size => 10 %><%= calendar_for('issue_start_date') %>
 </p>
+<% end %>
+
+<% if @safe_attributes.include?('due_date') %>
 <p>
   <label for='issue_due_date'><%= l(:field_due_date) %></label>
   <%= text_field_tag 'issue[due_date]', '', :size => 10 %><%= calendar_for('issue_due_date') %>
 </p>
-<% if Issue.use_field_for_done_ratio? %>
+<% end %>
+
+<% if @safe_attributes.include?('done_ratio') && Issue.use_field_for_done_ratio? %>
 <p>
   <label for='issue_done_ratio'><%= l(:field_done_ratio) %></label>
   <%= select_tag 'issue[done_ratio]', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %>
index 96e59b45146b1827be94ef273c01fbf36f69738b..4f53592b0957b6dfd5f3baaaf7ca9c13e9aeccfe 100644 (file)
         </p>
 
 <table class="attributes">
-<tr>
-    <th class="status"><%=l(:field_status)%>:</th><td class="status"><%= h(@issue.status.name) %></td>
-    <th class="start-date"><%=l(:field_start_date)%>:</th><td class="start-date"><%= format_date(@issue.start_date) %></td>
-</tr>
-<tr>
-    <th class="priority"><%=l(:field_priority)%>:</th><td class="priority"><%= h(@issue.priority.name) %></td>
-    <th class="due-date"><%=l(:field_due_date)%>:</th><td class="due-date"><%= format_date(@issue.due_date) %></td>
-</tr>
-<tr>
-    <th class="assigned-to"><%=l(:field_assigned_to)%>:</th><td class="assigned-to"><%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
-    <th class="progress"><%=l(:field_done_ratio)%>:</th><td class="progress"><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
-</tr>
-<tr>
-    <th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h(@issue.category ? @issue.category.name : "-") %></td>
-    <% if User.current.allowed_to?(:view_time_entries, @project) %>
-    <th class="spent-time"><%=l(:label_spent_time)%>:</th>
-    <td class="spent-time"><%= @issue.total_spent_hours > 0 ? (link_to l_hours(@issue.total_spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
-    <% else %>
-    <th></th>
-    <td></td>
-    <% end %>
-</tr>
-<tr>
-    <th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
-    <% if @issue.estimated_hours %>
-    <th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td>
-    <% end %>
-</tr>
+<%= issue_fields_rows do |rows|
+  rows.left l(:field_status), h(@issue.status.name), :class => 'status'
+  rows.left l(:field_priority), h(@issue.priority.name), :class => 'priority'
+
+  unless @issue.disabled_core_fields.include?('assigned_to_id')
+    rows.left l(:field_assigned_to), avatar(@issue.assigned_to, :size => "14").to_s + (@issue.assigned_to ? link_to_user(@issue.assigned_to) : "-"), :class => 'assigned-to'
+  end
+  unless @issue.disabled_core_fields.include?('category_id')
+    rows.left l(:field_category), h(@issue.category ? @issue.category.name : "-"), :class => 'category'
+  end
+  unless @issue.disabled_core_fields.include?('fixed_version_id')
+    rows.left l(:field_fixed_version), (@issue.fixed_version ? link_to_version(@issue.fixed_version) : "-"), :class => 'fixed-version'
+  end
+
+  unless @issue.disabled_core_fields.include?('start_date')
+    rows.right l(:field_start_date), format_date(@issue.start_date), :class => 'start-date'
+  end
+  unless @issue.disabled_core_fields.include?('due_date')
+    rows.right l(:field_due_date), format_date(@issue.due_date), :class => 'due-date'
+  end
+  unless @issue.disabled_core_fields.include?('done_ratio')
+    rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%"), :class => 'progress'
+  end
+  unless @issue.disabled_core_fields.include?('estimated_hours')
+    rows.right l(:field_estimated_hours), l_hours(@issue.estimated_hours), :class => 'estimated-hours'
+  end
+  if User.current.allowed_to?(:view_time_entries, @project)
+    rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? (link_to l_hours(@issue.total_spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-"), :class => 'spent-time'
+  end
+end %>
 <%= render_custom_fields_rows(@issue) %>
 <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
 </table>
index bfbed3b6b2d3f26e9417fece331e215bf1fb3bba..e8819f41d8dc0a2f0d66af6ab538af6e87c7c71d 100644 (file)
@@ -6,6 +6,17 @@
 <p><%= f.text_field :name, :required => true %></p>
 <p><%= f.check_box :is_in_roadmap %></p>
 
+<p>
+  <label><%= l(:field_core_fields) %></label>
+  <% Tracker::CORE_FIELDS.each do |field| %>
+  <label class="block">
+    <%= check_box_tag 'tracker[core_fields][]', field, @tracker.core_fields.include?(field) %>
+    <%= l("field_#{field}".sub(/_id$/, '')) %>
+  </label>
+  <% end %>
+</p>
+<%= hidden_field_tag 'tracker[core_fields][]', '' %>
+
 <% if IssueCustomField.all.any? %>
 <p>
   <label><%= l(:label_custom_field_plural) %></label>
index 25f2ca716b65501ee27aa04398f54fa9459c635e..03a8506773276dcbf7774f80a3bf82fff98aa4fc 100644 (file)
@@ -328,6 +328,7 @@ en:
   field_repository_is_default: Main repository
   field_multiple: Multiple values
   field_ldap_filter: LDAP filter
+  field_core_fields: Standard fields
 
   setting_app_title: Application title
   setting_app_subtitle: Application subtitle
index 5f9504b09ad34acad39b68389fa135a03cafb6e3..b341da62774ae9dd7ab62a18f39435d2199421ef 100644 (file)
@@ -327,6 +327,7 @@ fr:
   field_repository_is_default: Dépôt principal
   field_multiple: Valeurs multiples
   field_ldap_filter: Filtre LDAP
+  field_core_fields: Champs standards
 
   setting_app_title: Titre de l'application
   setting_app_subtitle: Sous-titre de l'application
diff --git a/db/migrate/20120705074331_add_trackers_fields_bits.rb b/db/migrate/20120705074331_add_trackers_fields_bits.rb
new file mode 100644 (file)
index 0000000..5d84583
--- /dev/null
@@ -0,0 +1,9 @@
+class AddTrackersFieldsBits < ActiveRecord::Migration
+  def self.up
+    add_column :trackers, :fields_bits, :integer, :default => 0
+  end
+
+  def self.down
+    remove_column :trackers, :fields_bits
+  end
+end
index 39ac46824cb49408f01e4b7478083ce449f5956b..466b0ccacc989fef608ab8eafd4ec57971eeb004 100644 (file)
@@ -18,6 +18,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 require 'iconv'
+require 'tcpdf'
 require 'fpdf/chinese'
 require 'fpdf/japanese'
 require 'fpdf/korean'
@@ -497,14 +498,13 @@ module Redmine
       # Returns a PDF string of a single issue
       def issue_to_pdf(issue)
         pdf = ITCPDF.new(current_language)
-        pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
+        pdf.SetTitle("#{issue.project} - #{issue.tracker} ##{issue.id}")
         pdf.alias_nb_pages
         pdf.footer_date = format_date(Date.today)
         pdf.AddPage
         pdf.SetFontStyle('B',11)
-        buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
+        buf = "#{issue.project} - #{issue.tracker} ##{issue.id}"
         pdf.RDMMultiCell(190, 5, buf)
-        pdf.Ln
         pdf.SetFontStyle('',8)
         base_x = pdf.GetX
         i = 1
@@ -514,62 +514,54 @@ module Redmine
           pdf.RDMMultiCell(190 - i, 5, buf)
           i += 1 if i < 35
         end
+        pdf.SetFontStyle('B',11)
+        pdf.RDMMultiCell(190 - i, 5, issue.subject.to_s)
+        pdf.SetFontStyle('',8)
+        pdf.RDMMultiCell(190, 5, "#{format_time(issue.created_on)} - #{issue.author}")
         pdf.Ln
 
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_status) + ":","LT")
-        pdf.SetFontStyle('',9)
-        pdf.RDMCell(60,5, issue.status.to_s,"RT")
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
-        pdf.SetFontStyle('',9)
-        pdf.RDMCell(60,5, issue.priority.to_s,"RT")
-        pdf.Ln
+        left = []
+        left << [l(:field_status), issue.status]
+        left << [l(:field_priority), issue.priority]
+        left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id')
+        left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id')
+        left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id')
 
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_author) + ":","L")
-        pdf.SetFontStyle('',9)
-        pdf.RDMCell(60,5, issue.author.to_s,"R")
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_category) + ":","L")
-        pdf.SetFontStyle('',9)
-        pdf.RDMCell(60,5, issue.category.to_s,"R")
-        pdf.Ln
+        right = []
+        right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date')
+        right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date')
+        right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio')
+        right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours')
+        right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project)
 
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
-        pdf.SetFontStyle('',9)
-        pdf.RDMCell(60,5, format_date(issue.created_on),"R")
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
-        pdf.SetFontStyle('',9)
-        pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
-        pdf.Ln
+        rows = left.size > right.size ? left.size : right.size
+        while left.size < rows
+          left << nil
+        end
+        while right.size < rows
+          right << nil
+        end
 
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
-        pdf.SetFontStyle('',9)
-        pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
-        pdf.SetFontStyle('',9)
-        pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
-        pdf.Ln
+        half = (issue.custom_field_values.size / 2.0).ceil
+        issue.custom_field_values.each_with_index do |custom_value, i|
+          (i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value)]
+        end
 
-        for custom_value in issue.custom_field_values
+        rows = left.size > right.size ? left.size : right.size
+        rows.times do |i|
+          item = left[i]
           pdf.SetFontStyle('B',9)
-          pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
+          pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
           pdf.SetFontStyle('',9)
-          pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
-        end
-
-        y0 = pdf.GetY
+          pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
 
-        pdf.SetFontStyle('B',9)
-        pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
-        pdf.SetFontStyle('',9)
-        pdf.RDMMultiCell(155,5, issue.subject,"RT")
-        pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
+          item = right[i]
+          pdf.SetFontStyle('B',9)
+          pdf.RDMCell(35,5, item ? "#{item.first}:" : "", i == 0 ? "LT" : "L")
+          pdf.SetFontStyle('',9)
+          pdf.RDMCell(60,5, item ? item.last.to_s : "", i == 0 ? "RT" : "R")
+          pdf.Ln
+        end
 
         pdf.SetFontStyle('B',9)
         pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
index 731d19b52a823f33b2aa3d2834de67bdb7b4a085..7393088aefb4f28536f93449dc68f2eb2936b4c3 100644 (file)
@@ -64,10 +64,21 @@ class TrackersControllerTest < ActionController::TestCase
     tracker = Tracker.first(:order => 'id DESC')
     assert_equal 'New tracker', tracker.name
     assert_equal [1], tracker.project_ids.sort
+    assert_equal Tracker::CORE_FIELDS, tracker.core_fields
     assert_equal [1, 6], tracker.custom_field_ids.sort
     assert_equal 0, tracker.workflows.count
   end
 
+  def create_with_disabled_core_fields
+    assert_difference 'Tracker.count' do
+      post :create, :tracker => { :name => 'New tracker', :core_fields => ['assigned_to_id', 'fixed_version_id', ''] }
+    end
+    assert_redirected_to :action => 'index'
+    tracker = Tracker.first(:order => 'id DESC')
+    assert_equal 'New tracker', tracker.name
+    assert_equal %w(assigned_to_id fixed_version_id), tracker.core_fields
+  end
+
   def test_create_new_with_workflow_copy
     assert_difference 'Tracker.count' do
       post :create, :tracker => { :name => 'New tracker' }, :copy_workflow_from => 1
@@ -107,6 +118,24 @@ class TrackersControllerTest < ActionController::TestCase
                                         :type => 'hidden'}
   end
 
+  def test_edit_should_check_core_fields
+    tracker = Tracker.find(1)
+    tracker.core_fields = %w(assigned_to_id fixed_version_id)
+    tracker.save!
+
+    get :edit, :id => 1
+    assert_response :success
+    assert_template 'edit'
+
+    assert_select 'input[name=?][value=assigned_to_id][checked=checked]', 'tracker[core_fields][]'
+    assert_select 'input[name=?][value=fixed_version_id][checked=checked]', 'tracker[core_fields][]'
+
+    assert_select 'input[name=?][value=category_id]', 'tracker[core_fields][]'
+    assert_select 'input[name=?][value=category_id][checked=checked]', 'tracker[core_fields][]', 0
+
+    assert_select 'input[name=?][value=][type=hidden]', 'tracker[core_fields][]'
+  end
+
   def test_update
     put :update, :id => 1, :tracker => { :name => 'Renamed',
                                         :project_ids => ['1', '2', ''] }
@@ -121,6 +150,12 @@ class TrackersControllerTest < ActionController::TestCase
     assert Tracker.find(1).project_ids.empty?
   end
 
+  def test_update_without_core_fields
+    put :update, :id => 1, :tracker => { :name => 'Renamed', :core_fields => [''] }
+    assert_redirected_to :action => 'index'
+    assert Tracker.find(1).core_fields.empty?
+  end
+
   def test_update_with_failure
     put :update, :id => 1, :tracker => { :name => '' }
     assert_response :success
index b5799f3fc8e3c9e8076f3cc577dd98215f92347e..fe8bdf2acdb243f70a060499523cbcc156fd6936 100644 (file)
@@ -403,6 +403,52 @@ class IssueTest < ActiveSupport::TestCase
     assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
   end
 
+  def test_safe_attributes_should_not_include_disabled_field
+    tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
+
+    issue = Issue.new(:tracker => tracker)
+    assert_include 'tracker_id', issue.safe_attribute_names
+    assert_include 'status_id', issue.safe_attribute_names
+    assert_include 'subject', issue.safe_attribute_names
+    assert_include 'description', issue.safe_attribute_names
+    assert_include 'custom_field_values', issue.safe_attribute_names
+    assert_include 'custom_fields', issue.safe_attribute_names
+    assert_include 'lock_version', issue.safe_attribute_names
+
+    tracker.core_fields.each do |field|
+      assert_include field, issue.safe_attribute_names
+    end
+
+    tracker.disabled_core_fields.each do |field|
+      assert_not_include field, issue.safe_attribute_names
+    end
+  end
+
+  def test_safe_attributes_should_ignore_disabled_fields
+    tracker = Tracker.find(1)
+    tracker.core_fields = %w(assigned_to_id due_date)
+    tracker.save!
+
+    issue = Issue.new(:tracker => tracker)
+    issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
+    assert_nil issue.start_date
+    assert_equal Date.parse('2012-07-14'), issue.due_date
+  end
+
+  def test_safe_attributes_should_accept_target_tracker_fields
+    source = Tracker.find(1)
+    source.core_fields = []
+    source.save!
+    target = Tracker.find(2)
+    target.core_fields = %w(assigned_to_id due_date)
+    target.save!
+
+    issue = Issue.new(:tracker => source)
+    issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
+    assert_equal target, issue.tracker
+    assert_equal Date.parse('2012-07-14'), issue.due_date
+  end
+
   def test_copy
     issue = Issue.new.copy_from(1)
     assert issue.copy?
index 9cf6866a9a16dd304d1db4006410112a2ebd8c14..91b259827890a3f5ccb31d35721e6b3e6eea4d1c 100644 (file)
@@ -59,6 +59,30 @@ class TrackerTest < ActiveSupport::TestCase
     assert_equal [], Tracker.new.issue_statuses
   end
 
+  def test_core_fields_should_be_enabled_by_default
+    tracker = Tracker.new
+    assert_equal Tracker::CORE_FIELDS, tracker.core_fields
+    assert_equal [], tracker.disabled_core_fields
+  end
+
+  def test_core_fields
+    tracker = Tracker.new
+    tracker.core_fields = %w(assigned_to_id due_date)
+
+    assert_equal %w(assigned_to_id due_date), tracker.core_fields
+    assert_equal Tracker::CORE_FIELDS - %w(assigned_to_id due_date), tracker.disabled_core_fields
+  end
+
+  def test_core_fields_should_return_fields_enabled_for_any_tracker
+    trackers = []
+    trackers << Tracker.new(:core_fields => %w(assigned_to_id due_date))
+    trackers << Tracker.new(:core_fields => %w(assigned_to_id done_ratio))
+    trackers << Tracker.new(:core_fields => [])
+
+    assert_equal %w(assigned_to_id due_date done_ratio), Tracker.core_fields(trackers)
+    assert_equal Tracker::CORE_FIELDS - %w(assigned_to_id due_date done_ratio), Tracker.disabled_core_fields(trackers)
+  end
+
   def test_sort_should_sort_by_position
     a = Tracker.new(:name => 'Tracker A', :position => 2)
     b = Tracker.new(:name => 'Tracker B', :position => 1)