]> source.dussan.org Git - redmine.git/commitdiff
Option for long text custom fields to be displayed under the description field (...
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Wed, 25 Jan 2017 14:55:36 +0000 (14:55 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Wed, 25 Jan 2017 14:55:36 +0000 (14:55 +0000)
Based on patch by Marius BALTEANU.

git-svn-id: http://svn.redmine.org/redmine/trunk@16251 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/issues_helper.rb
app/models/custom_field.rb
app/views/custom_fields/formats/_text.html.erb
app/views/issues/_form_custom_fields.html.erb
app/views/issues/show.html.erb
config/locales/en.yml
config/locales/fr.yml
lib/redmine/field_format.rb
public/stylesheets/application.css
test/functional/custom_fields_controller_test.rb
test/functional/issues_controller_test.rb

index 122c2ffcaa5794fa21b133421976d8ec7f4e72cf..29f4691211536a7e6c9cecea2c6a41627795ca50 100644 (file)
@@ -239,8 +239,8 @@ module IssuesHelper
     r.to_html
   end
 
-  def render_custom_fields_rows(issue)
-    values = issue.visible_custom_field_values
+  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|
@@ -252,6 +252,26 @@ module IssuesHelper
     end
   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?
+
+    s = ''
+    values.each_with_index do |value, i|
+      if value.custom_field.text_formatting == 'full'
+        attr_value = content_tag('div', show_value(value), class: 'wiki')
+      else
+        attr_value = show_value(value)
+      end
+      content =
+          content_tag('hr') +
+          content_tag('p', content_tag('strong', custom_field_name_tag(value.custom_field) )) +
+          content_tag('div', attr_value, class: 'value')
+      s << content_tag('div', content, class: "cf_#{value.custom_field.id} attribute")
+    end
+    s.html_safe
+  end
+
   # Returns the path for updating the issue form
   # with project as the current project
   def update_issue_form_path(project, issue)
index 717df95e8ee761b99d36b91a6e7e7f32a2659b16..72cf0ea7c680d2b0e6093323bbe84166fd88c264 100644 (file)
@@ -89,7 +89,8 @@ class CustomField < ActiveRecord::Base
     'edit_tag_style',
     'user_role',
     'version_status',
-    'extensions_allowed'
+    'extensions_allowed',
+    'full_width_layout'
 
   def format
     @format ||= Redmine::FieldFormat.find(field_format)
@@ -186,6 +187,10 @@ class CustomField < ActiveRecord::Base
     format.totalable_supported
   end
 
+  def full_width_layout?
+    full_width_layout == '1'
+  end
+
   # Returns a ORDER BY clause that can used to sort customized
   # objects by their value of the custom field.
   # Returns nil if the custom field can not be used for sorting.
index e72dcab4258ae1ac82d0f652747f9e7e2022372b..79ea7c5e6e5db0db6a95ff9a0bb55a4d59775f36 100644 (file)
@@ -1,3 +1,6 @@
 <%= render :partial => 'custom_fields/formats/regexp', :locals => {:f => f, :custom_field => custom_field} %>
 <p><%= f.check_box :text_formatting, {:label => :setting_text_formatting}, 'full', '' %></p>
+<% if @custom_field.class.name == "IssueCustomField" %>
+<p><%= f.check_box :full_width_layout %></p>
+<% end %>
 <p><%= f.text_area(:default_value, :rows => 5) %></p>
index 2e12c00e8b76deaef76677dfb34acbcd31fd3db9..13bedd5465537fa61bca48fd6ca591de00ce9fd1 100644 (file)
@@ -1,4 +1,7 @@
 <% 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? %>
 <div class="splitcontent">
 <div class="splitcontentleft">
@@ -14,3 +17,7 @@
 </div>
 </div>
 <% end %>
+
+<% custom_field_values_full_width.each do |value| %>
+  <p><%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %></p>
+<% end %>
index b9e5111faed633c8ac2eacac0f6f85539e330417..9d767f0b11cf3885c808bec573a62b283658ff83 100644 (file)
@@ -66,7 +66,7 @@
     end
   end
 end %>
-<%= render_custom_fields_rows(@issue) %>
+<%= render_half_width_custom_fields_rows(@issue) %>
 <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
 </div>
 
@@ -87,6 +87,8 @@ end %>
 <%= link_to_attachments @issue, :thumbnails => true %>
 <% end -%>
 
+<%= render_full_width_custom_fields_rows(@issue) %>
+
 <%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
 
 <% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
index 24d73e65d92a19662213a5780b9b07c7844a570c..fb9b6bf1fd15db81ab92acf38e65cdc770bd540b 100644 (file)
@@ -372,6 +372,7 @@ en:
   field_textarea_font: Font used for text areas
   field_updated_by: Updated by
   field_last_updated_by: Last updated by
+  field_full_width_layout: Full width layout
 
   setting_app_title: Application title
   setting_app_subtitle: Application subtitle
index 6136c797852de88109750776ba0381ad3c440d46..b1e0239c784ef09bed5a7db16f10c1dd40f0ac73 100644 (file)
@@ -384,6 +384,7 @@ fr:
   field_textarea_font: Police utilisée pour les champs texte
   field_updated_by: Mise à jour par
   field_last_updated_by: Dernière mise à jour par
+  field_full_width_layout: Afficher sur toute la largeur
 
   setting_app_title: Titre de l'application
   setting_app_subtitle: Sous-titre de l'application
index fee2c978ccce974e226cfaf3b8854e32b0699b37..ea9d7169c9df514051826b322ed78149fc3144b6 100644 (file)
@@ -108,7 +108,7 @@ module Redmine
         CustomField.store_accessor :format_store, *args
       end
 
-      field_attributes :url_pattern
+      field_attributes :url_pattern, :full_width_layout
 
       def name
         self.class.format_name
index dfe22a74c1d43dc3eba3cb021180f7a6f1de56dd..d16c2cf160ce2f39d763022df5a74c9142a34c71 100644 (file)
@@ -463,8 +463,8 @@ div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
 div.issue span.private, div.journal span.private { position:relative; bottom: 2px; text-transform: uppercase; background: #d22; color: #fff; font-weight:bold; padding: 0px 2px 0px 2px; font-size: 60%; margin-right: 2px; border-radius: 2px;}
 div.issue .next-prev-links {color:#999;}
 div.issue .attributes {margin-top: 2em;}
-div.issue .attribute {padding-left:180px; clear:left; min-height: 1.8em;}
-div.issue .attribute .label {width: 170px; margin-left:-180px; font-weight:bold; float:left; overflow:hidden; text-overflow: ellipsis;}
+div.issue .attributes .attribute {padding-left:180px; clear:left; min-height: 1.8em;}
+div.issue .attributes .attribute .label {width: 170px; margin-left:-180px; font-weight:bold; float:left;  overflow:hidden; text-overflow: ellipsis;}
 div.issue .attribute .value {overflow:hidden; text-overflow: ellipsis;}
 div.issue.overdue .due-date .value { color: #c22; }
 
index f13e7d0f8997a9c3a1f177bc7fcb5174f2184d9d..70fbb837e09ccf0f4daf7f13514f93581c4b4887 100644 (file)
@@ -126,6 +126,20 @@ class CustomFieldsControllerTest < Redmine::ControllerTest
     assert_select '[name=?]', 'custom_field[default_value]', 0
   end
 
+  def test_setting_full_width_layout_shoul_be_present_only_for_long_text_issue_custom_field
+    get :new, :type => 'IssueCustomField', :custom_field => {:field_format => 'text'}
+    assert_response :success
+    assert_select '[name=?]', 'custom_field[full_width_layout]'
+
+    get :new, :type => 'IssueCustomField', :custom_field => {:field_format => 'list'}
+    assert_response :success
+    assert_select '[name=?]', 'custom_field[full_width_layout]', 0
+
+    get :new, :type => 'TimeEntryCustomField', :custom_field => {:field_format => 'text'}
+    assert_response :success
+    assert_select '[name=?]', 'custom_field[full_width_layout]', 0
+  end
+
   def test_new_js
     xhr :get, :new, :type => 'IssueCustomField', :custom_field => {:field_format => 'list'}, :format => 'js'
     assert_response :success
index 4e8d88668957dfe4da8f736ec35fc492cff54250..455bf834a6918cb6bfdf4898bece85e465701f07 100644 (file)
@@ -561,8 +561,8 @@ class IssuesControllerTest < Redmine::ControllerTest
       str_big5  = "\xa4@\xa4\xeb".force_encoding('Big5')
       issue = Issue.generate!(:subject => str_utf8)
 
-      get :index, :project_id => 1, 
-                  :f => ['subject'], 
+      get :index, :project_id => 1,
+                  :f => ['subject'],
                   :op => '=', :values => [str_utf8],
                   :format => 'csv'
       assert_equal 'text/csv; header=present', @response.content_type
@@ -580,8 +580,8 @@ class IssuesControllerTest < Redmine::ControllerTest
       str_utf8  = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
       issue = Issue.generate!(:subject => str_utf8)
 
-      get :index, :project_id => 1, 
-                  :f => ['subject'], 
+      get :index, :project_id => 1,
+                  :f => ['subject'],
                   :op => '=', :values => [str_utf8],
                   :c => ['status', 'subject'],
                   :format => 'csv',
@@ -603,8 +603,8 @@ class IssuesControllerTest < Redmine::ControllerTest
       str1  = "test_index_csv_tw"
       issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
 
-      get :index, :project_id => 1, 
-                  :f => ['subject'], 
+      get :index, :project_id => 1,
+                  :f => ['subject'],
                   :op => '=', :values => [str1],
                   :c => ['estimated_hours', 'subject'],
                   :format => 'csv',
@@ -620,8 +620,8 @@ class IssuesControllerTest < Redmine::ControllerTest
       str1  = "test_index_csv_fr"
       issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
 
-      get :index, :project_id => 1, 
-                  :f => ['subject'], 
+      get :index, :project_id => 1,
+                  :f => ['subject'],
                   :op => '=', :values => [str1],
                   :c => ['estimated_hours', 'subject'],
                   :format => 'csv',
@@ -696,34 +696,34 @@ class IssuesControllerTest < Redmine::ControllerTest
       assert_response :success
     end
   end
-  
+
   def test_index_sort_by_assigned_to
     get :index, :sort => 'assigned_to'
     assert_response :success
-    
+
     assignees = issues_in_list.map(&:assigned_to).compact
     assert_equal assignees.sort, assignees
     assert_select 'table.issues.sort-by-assigned-to.sort-asc'
   end
-  
+
   def test_index_sort_by_assigned_to_desc
     get :index, :sort => 'assigned_to:desc'
     assert_response :success
-    
+
     assignees = issues_in_list.map(&:assigned_to).compact
     assert_equal assignees.sort.reverse, assignees
     assert_select 'table.issues.sort-by-assigned-to.sort-desc'
   end
-  
+
   def test_index_group_by_assigned_to
     get :index, :group_by => 'assigned_to', :sort => 'priority'
     assert_response :success
   end
-  
+
   def test_index_sort_by_author
     get :index, :sort => 'author', :c => ['author']
     assert_response :success
-    
+
     authors = issues_in_list.map(&:author)
     assert_equal authors.sort, authors
   end
@@ -731,30 +731,30 @@ class IssuesControllerTest < Redmine::ControllerTest
   def test_index_sort_by_author_desc
     get :index, :sort => 'author:desc'
     assert_response :success
-    
+
     authors = issues_in_list.map(&:author)
     assert_equal authors.sort.reverse, authors
   end
-  
+
   def test_index_group_by_author
     get :index, :group_by => 'author', :sort => 'priority'
     assert_response :success
   end
-  
+
   def test_index_sort_by_spent_hours
     get :index, :sort => 'spent_hours:desc'
     assert_response :success
     hours = issues_in_list.map(&:spent_hours)
     assert_equal hours.sort.reverse, hours
   end
-  
+
   def test_index_sort_by_total_spent_hours
     get :index, :sort => 'total_spent_hours:desc'
     assert_response :success
     hours = issues_in_list.map(&:total_spent_hours)
     assert_equal hours.sort.reverse, hours
   end
-  
+
   def test_index_sort_by_total_estimated_hours
     get :index, :sort => 'total_estimated_hours:desc'
     assert_response :success
@@ -1093,7 +1093,7 @@ class IssuesControllerTest < Redmine::ControllerTest
   def test_index_should_not_include_new_issue_tab_for_project_without_trackers
     with_settings :new_item_menu_tab => '1' do
       Project.find(1).trackers.clear
-  
+
       @request.session[:user_id] = 2
       get :index, :project_id => 1
       assert_select '#main-menu a.new-issue', 0
@@ -1105,7 +1105,7 @@ class IssuesControllerTest < Redmine::ControllerTest
       role = Role.find(1)
       role.remove_permission! :add_issues
       role.add_permission! :copy_issues
-  
+
       @request.session[:user_id] = 2
       get :index, :project_id => 1
       assert_select '#main-menu a.new-issue', 0
@@ -1381,7 +1381,7 @@ class IssuesControllerTest < Redmine::ControllerTest
 
   def test_show_should_display_prev_next_links_with_query_and_sort_on_association
     @request.session[:issue_query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
-    
+
     %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
       @request.session['issues_index_sort'] = assoc_sort
 
@@ -1575,6 +1575,25 @@ class IssuesControllerTest < Redmine::ControllerTest
     assert_select ".cf_1 .value", :text => 'MySQL, Oracle'
   end
 
+  def test_show_with_full_width_layout_custom_field_should_show_field_under_description
+    field = IssueCustomField.create!(:name => 'Long text', :field_format => 'text', :full_width_layout => '1',
+      :tracker_ids => [1], :is_for_all => true)
+    issue = Issue.find(1)
+    issue.custom_field_values = {field.id => 'This is a long text'}
+    issue.save!
+
+    get :show, :id => 1
+    assert_response :success
+
+    # long text custom field should not be render in the attributes div
+    assert_select "div.attributes div.attribute.cf_#{field.id} p strong", 0, :text => 'Long text'
+    assert_select "div.attributes div.attribute.cf_#{field.id} div.value", 0, :text => 'This is a long text'
+
+    # long text custom field should be render under description field
+    assert_select "div.description ~ div.attribute.cf_#{field.id} p strong", :text => 'Long text'
+    assert_select "div.description ~ div.attribute.cf_#{field.id} div.value", :text => 'This is a long text'
+  end
+
   def test_show_with_multi_user_custom_field
     field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
       :tracker_ids => [1], :is_for_all => true)
@@ -1628,7 +1647,7 @@ class IssuesControllerTest < Redmine::ControllerTest
   end
 
   def test_show_export_to_pdf
-    issue = Issue.find(3) 
+    issue = Issue.find(3)
     assert issue.relations.select{|r| r.other_issue(issue).visible?}.present?
     get :show, :id => 3, :format => 'pdf'
     assert_response :success
@@ -2076,7 +2095,7 @@ class IssuesControllerTest < Redmine::ControllerTest
     get :new, :project_id => 'invalid'
     assert_response 404
   end
+
   def test_new_with_parent_id_should_only_propose_valid_trackers
     @request.session[:user_id] = 2
     t = Tracker.find(3)
@@ -2625,7 +2644,7 @@ class IssuesControllerTest < Redmine::ControllerTest
                               :custom_field_values => {'2' => 'Value for field 2'}}
       end
       assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
-  
+
       assert_equal 1, ActionMailer::Base.deliveries.size
     end
   end
@@ -2899,7 +2918,7 @@ class IssuesControllerTest < Redmine::ControllerTest
         assert_select 'option[value="1"][selected=selected]', :text => 'eCookbook'
         assert_select 'option[value="2"]:not([selected])', :text => 'OnlineStore'
       end
-      assert_select 'input[name=?][value=?]', 'issue[subject]', orig.subject 
+      assert_select 'input[name=?][value=?]', 'issue[subject]', orig.subject
       assert_select 'input[name=copy_from][value="1"]'
     end
   end
@@ -3194,7 +3213,7 @@ class IssuesControllerTest < Redmine::ControllerTest
   def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
     @request.session[:user_id] = 2
     Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
-    
+
     get :edit, :id => 1
     assert_select 'input[name=?]', 'time_entry[hours]'
   end
@@ -3202,7 +3221,7 @@ class IssuesControllerTest < Redmine::ControllerTest
   def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
     @request.session[:user_id] = 2
     Role.find_by_name('Manager').remove_permission! :log_time
-    
+
     get :edit, :id => 1
     assert_select 'input[name=?]', 'time_entry[hours]', 0
   end
@@ -3847,7 +3866,7 @@ class IssuesControllerTest < Redmine::ControllerTest
     assert_response :redirect
     assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
   end
+
   def test_put_update_should_redirect_with_previous_and_next_issue_ids_params
     @request.session[:user_id] = 2
 
@@ -3901,17 +3920,17 @@ class IssuesControllerTest < Redmine::ControllerTest
 
       assert_select 'select[name=?]', 'issue[project_id]'
       assert_select 'input[name=?]', 'issue[parent_issue_id]'
-  
+
       # Project specific custom field, date type
       field = CustomField.find(9)
       assert !field.is_for_all?
       assert_equal 'date', field.field_format
       assert_select 'input[name=?]', 'issue[custom_field_values][9]'
-  
+
       # System wide custom field
       assert CustomField.find(1).is_for_all?
       assert_select 'select[name=?]', 'issue[custom_field_values][1]'
-  
+
       # Be sure we don't display inactive IssuePriorities
       assert ! IssuePriority.find(15).active?
       assert_select 'select[name=?]', 'issue[priority_id]' do
@@ -4115,7 +4134,7 @@ class IssuesControllerTest < Redmine::ControllerTest
                                        :issue => {:priority_id => '',
                                                   :assigned_to_id => group.id,
                                                   :custom_field_values => {'2' => ''}}
-  
+
       assert_response 302
       assert_equal [group, group], Issue.where(:id => [1, 2]).collect {|i| i.assigned_to}
     end
@@ -4421,7 +4440,7 @@ class IssuesControllerTest < Redmine::ControllerTest
       assert_select 'option[value="2"]'
     end
   end
-  
+
   def test_bulk_copy_to_another_project
     @request.session[:user_id] = 2
     assert_difference 'Issue.count', 2 do
@@ -4474,7 +4493,7 @@ class IssuesControllerTest < Redmine::ControllerTest
                     :assigned_to_id => 2)
     ]
     assert_difference 'Issue.count', issues.size do
-      post :bulk_update, :ids => issues.map(&:id), :copy => '1', 
+      post :bulk_update, :ids => issues.map(&:id), :copy => '1',
            :issue => {
              :project_id => '', :tracker_id => '', :assigned_to_id => '',
              :status_id => '', :start_date => '', :due_date => ''
@@ -4506,7 +4525,7 @@ class IssuesControllerTest < Redmine::ControllerTest
     @request.session[:user_id] = 2
     assert_difference 'Issue.count', 2 do
       assert_no_difference 'Project.find(1).issues.count' do
-        post :bulk_update, :ids => [1, 2], :copy => '1', 
+        post :bulk_update, :ids => [1, 2], :copy => '1',
              :issue => {
                :project_id => '2', :tracker_id => '', :assigned_to_id => '2',
                :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'