diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb old mode 100644 new mode 100755 index 122c2ff..b8f0cc0 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -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.select{|cf| cf.custom_field.full_width_layout == false} 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{|cf| cf.custom_field.full_width_layout == true} + 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) diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb old mode 100644 new mode 100755 index 400ee56..6c8c352 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -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) diff --git a/app/views/custom_fields/formats/_text.html.erb b/app/views/custom_fields/formats/_text.html.erb old mode 100644 new mode 100755 index e72dcab..2f8968a --- a/app/views/custom_fields/formats/_text.html.erb +++ b/app/views/custom_fields/formats/_text.html.erb @@ -1,3 +1,6 @@ <%= render :partial => 'custom_fields/formats/regexp', :locals => {:f => f, :custom_field => custom_field} %>

<%= f.check_box :text_formatting, {:label => :setting_text_formatting}, 'full', '' %>

+<% if @custom_field.class.name == "IssueCustomField" %> +

<%= f.check_box :full_width_layout, {:label => :setting_full_width_layout} %>

+<% end %>

<%= f.text_area(:default_value, :rows => 5) %>

diff --git a/app/views/issues/_form_custom_fields.html.erb b/app/views/issues/_form_custom_fields.html.erb old mode 100644 new mode 100755 index 2e12c00..65f564c --- a/app/views/issues/_form_custom_fields.html.erb +++ b/app/views/issues/_form_custom_fields.html.erb @@ -1,10 +1,12 @@ <% custom_field_values = @issue.editable_custom_field_values %> +<% custom_field_values_half_width = custom_field_values.select{ |cf| cf.custom_field.full_width_layout == false } %> +<% custom_field_values_full_width = custom_field_values.select{ |cf| cf.custom_field.full_width_layout == true } %> <% if custom_field_values.present? %>
<% i = 0 %> -<% split_on = (custom_field_values.size / 2.0).ceil - 1 %> -<% custom_field_values.each do |value| %> +<% split_on = (custom_field_values_half_width.size / 2.0).ceil - 1 %> +<% custom_field_values_half_width.each do |value| %>

<%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %>

<% if i == split_on -%>
@@ -12,5 +14,8 @@ <% i += 1 -%> <% end -%>
+<% custom_field_values_full_width.each do |value| %> +

<%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %>

+<% end %>
<% end %> diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb old mode 100644 new mode 100755 index b9e5111..9d767f0 --- a/app/views/issues/show.html.erb +++ b/app/views/issues/show.html.erb @@ -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) %> @@ -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) %> diff --git a/config/locales/en.yml b/config/locales/en.yml old mode 100644 new mode 100755 index b5f771d..0b03fdc --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -453,6 +453,7 @@ en: setting_attachment_extensions_denied: Disallowed extensions setting_new_item_menu_tab: Project menu tab for creating new objects setting_commit_logs_formatting: Apply text formatting to commit messages + setting_full_width_layout: Full width layout permission_add_project: Create project permission_add_subprojects: Create subprojects diff --git a/db/migrate/20161228180443_add_custom_fields_full_width_layout.rb b/db/migrate/20161228180443_add_custom_fields_full_width_layout.rb new file mode 100755 index 0000000..1f1c968 --- /dev/null +++ b/db/migrate/20161228180443_add_custom_fields_full_width_layout.rb @@ -0,0 +1,9 @@ +class AddCustomFieldsFullWidthLayout < ActiveRecord::Migration + def self.up + add_column :custom_fields, :full_width_layout, :boolean, :default => false + end + + def self.down + remove_column :custom_fields, :full_width_layout + end +end diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css old mode 100644 new mode 100755 index 701be55..a0d2498 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -457,8 +457,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;} div.issue .attribute .value {overflow:hidden; text-overflow: ellipsis;} div.issue.overdue .due-date .value { color: #c22; } diff --git a/test/functional/custom_fields_controller_test.rb b/test/functional/custom_fields_controller_test.rb old mode 100644 new mode 100755 index f13e7d0..70fbb83 --- a/test/functional/custom_fields_controller_test.rb +++ b/test/functional/custom_fields_controller_test.rb @@ -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 diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb old mode 100644 new mode 100755 index 3e1bfa2..7fb0541 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -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 => true, + :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) @@ -1614,7 +1633,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 @@ -2062,7 +2081,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) @@ -2611,7 +2630,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 @@ -2885,7 +2904,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 @@ -3180,7 +3199,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 @@ -3188,7 +3207,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 @@ -3833,7 +3852,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 @@ -3887,17 +3906,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 @@ -4084,7 +4103,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 @@ -4390,7 +4409,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 @@ -4443,7 +4462,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 => '' @@ -4475,7 +4494,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'