Index: app/helpers/projects_helper.rb =================================================================== --- app/helpers/projects_helper.rb +++ app/helpers/projects_helper.rb @@ -22,6 +22,7 @@ tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural}, {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural}, {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural}, + {:name => 'custom_fields_layouts', :action => :manage_custom_fields_layout, :partial => 'projects/settings/custom_fields_layout', :label => :label_custom_field_plural}, {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural}, {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural}, {:name => 'resolutions', :action => :manage_resolutions, :partial => 'projects/settings/issue_resolutions', :label => :label_issue_resolution_plural}, Index: app/helpers/custom_fields_helper.rb =================================================================== --- app/helpers/custom_fields_helper.rb +++ app/helpers/custom_fields_helper.rb @@ -85,7 +85,7 @@ content_tag "label", content + (required ? " *".html_safe : ""), - :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}" + :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}", :class => "custom_field_label" end # Return custom field tag with its label tag Index: app/models/custom_field_layout.rb =================================================================== --- app/models/custom_field_layout.rb +++ app/models/custom_field_layout.rb @@ -0,0 +1,3 @@ +class CustomFieldLayout < ActiveRecord::Base + +end Index: app/controllers/custom_fields_layouts_controller.rb =================================================================== --- app/controllers/custom_fields_layouts_controller.rb +++ app/controllers/custom_fields_layouts_controller.rb @@ -0,0 +1,60 @@ +class CustomFieldsLayoutsController < ApplicationController + helper :all + before_filter :find_custom_field_values + + def new + @layout = CustomFieldLayout.new(project_id: @project.id, tracker_id: @tracker.id) + end + + def apply + if @cf_layout.nil? + @layout = CustomFieldLayout.new(project_id: @project.id, tracker_id: @tracker.id) + @layout.location = JSON.parse(params[:ids]) + @layout.project_id = @project.id + @layout.tracker_id = @tracker.id + if @layout.save + redirect_to settings_project_path(@project) + end + else + @cf_layout.location = JSON.parse(params[:ids]) + if @cf_layout.save + redirect_to settings_project_path(@project) + end + end + end + + def show + respond_to do |format| + format.js + end + end + + private + def find_custom_field_values + @project = Project.find(params[:project_id]) + @tracker = Tracker.find(params[:layout][:tracker_id]) + + #get last edited custom field location + @cf_layout = CustomFieldLayout.find_by({:tracker_id => @tracker.id, :project_id => @project.id}) + cf_ids = @cf_layout.nil? ? [[],[]] : JSON.parse(@cf_layout.location.gsub("nil","null")) + + #get ids of nil elements + ids_of_nil = [[],[]] + cf_ids.each_index{ |i| cf_ids[i].each_index.select{|k| ids_of_nil[i] << k if cf_ids[i][k].nil?}}.map!{|i| i -= [nil]} + + #get default custom field location + default_values = (@project.issues.new(:tracker_id => @tracker.id, + :subject => " ", :author_id => @current_user).editable_custom_field_values) + + #if not set yet then default + if !cf_ids.all?{|array| array.empty?} + ordered_values = default_values.group_by(&:custom_field_id).values_at(*cf_ids.flatten).flatten(1) + added = default_values - ordered_values + @custom_fields_values = [ordered_values[0 ... cf_ids.first.count], + ordered_values[cf_ids.first.count .. -1] + added] + ids_of_nil.each_index{|i| ids_of_nil[i].each{|v| @custom_fields_values[i].insert(v,nil)}} + else + @custom_fields_values = [default_values[0 ... default_values.count/2], default_values[default_values.count/2 .. -1]] + end + end +end Index: app/controllers/projects_controller.rb =================================================================== --- app/controllers/projects_controller.rb +++ app/controllers/projects_controller.rb @@ -161,6 +161,8 @@ @member ||= @project.members.new @trackers = Tracker.sorted.to_a @wiki ||= @project.wiki || Wiki.new(:project => @project) + @layout = CustomFieldLayout.new() + end def edit Index: app/views/custom_fields_layouts/show.js.erb =================================================================== --- app/views/custom_fields_layouts/show.js.erb +++ app/views/custom_fields_layouts/show.js.erb @@ -0,0 +1,2 @@ +$("#cfv").html('<%= escape_javascript(render(partial: "show"))%>'); + Index: app/views/custom_fields_layouts/_custom_fields_layout.html.erb =================================================================== --- app/views/custom_fields_layouts/_custom_fields_layout.html.erb +++ app/views/custom_fields_layouts/_custom_fields_layout.html.erb @@ -0,0 +1,24 @@ +
+
+
+ <% i = 0 %> + <% custom_fields_values.each do |side|%> + <% side.each do |value|%> + <% if !value.nil? %> +

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

+ <%else %> +

+ <% end %> + <% end -%> + <% i = i + 1 %> + <% if (i == 1) %> +
+ <% end -%> + <% end -%> +
+

+
+
+ Index: app/views/custom_fields_layouts/_show.html.erb =================================================================== --- app/views/custom_fields_layouts/_show.html.erb +++ app/views/custom_fields_layouts/_show.html.erb @@ -0,0 +1,5 @@ +<% issue = @project.issues.new(tracker_id: @tracker.id, subject: " ", author_id: @current_user) %> + +<%= render partial: "custom_fields_layout" , locals: {issue: issue, custom_fields_values: @custom_fields_values} %> + + Index: app/views/projects/settings/_custom_fields_layout.html.erb =================================================================== --- app/views/projects/settings/_custom_fields_layout.html.erb +++ app/views/projects/settings/_custom_fields_layout.html.erb @@ -0,0 +1,134 @@ +

<%= l(:label_custom_field_layout) %>

+ + +
+ +<%= labelled_form_for @layout,:as => 'layout', url: {controller: "custom_fields_layouts", + action: "apply", :project_id => @project} do |f| %> + + <% include_calendar_headers_tags %> + <% if @project.issues.new.safe_attribute? 'tracker_id' %> +

<%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, {:required => true}, + :onchange => "getCustomFields();" %>

+ <%end%> + +

+ +
+ + <%= hidden_field_tag :ids, "", :id => "custom_fields_locations" %> + <%= submit_tag l(:button_update), :onclick => "setCfIds();" %> + +
+ +<%end %> +
Index: app/views/issues/show.html.erb =================================================================== --- app/views/issues/show.html.erb +++ app/views/issues/show.html.erb @@ -72,6 +72,8 @@ rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? link_to(l_hours(@issue.total_spent_hours), issue_time_entries_path(@issue)) : "-"), :class => 'spent-time' end end %> + + <%= render_custom_fields_rows(@issue) %> <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %> + + Index: lib/redmine.rb =================================================================== --- lib/redmine.rb +++ lib/redmine.rb @@ -86,7 +86,8 @@ map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :update, :destroy, :autocomplete]}, :require => :member map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member - + map.permission :manage_custom_fields_layout, {:projects => :settings, :custom_fields_editing => [:apply]}, :require => :member + map.project_module :issue_tracking do |map| # Issues map.permission :view_issues, {:issues => [:index, :show], Index: app/views/issues/_form_custom_fields.html.erb =================================================================== --- app/views/issues/_form_custom_fields.html.erb +++ app/views/issues/_form_custom_fields.html.erb @@ -1,16 +1,20 @@ -<% custom_field_values = @issue.editable_custom_field_values %> -<% if custom_field_values.present? %> -
-
-<% i = 0 %> -<% split_on = (custom_field_values.size / 2.0).ceil - 1 %> -<% custom_field_values.each do |value| %> -

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

-<% if i == split_on -%> -
-<% end -%> -<% i += 1 -%> -<% end -%> -
-
-<% end %> +<% cf_layout = CustomFieldLayout.all.where(:tracker_id => @issue.tracker_id, :project_id => @project.id).last %> +<% default_values = @issue.editable_custom_field_values %> +<% cf_ids = cf_layout.nil? ? [[],[]] : JSON.parse(cf_layout.location.gsub("nil","null")) %> + +<% ids_of_nil = [[],[]] %> +<% cf_ids.each_index{ |i| cf_ids[i].each_index.select{|k| ids_of_nil[i] << k if cf_ids[i][k].nil?}}.map!{|i| i -= [nil]} %> + +<% if !cf_ids.all?{|array| array.empty?} %> +<% ordered_values = default_values.group_by(&:custom_field_id).values_at(*cf_ids.flatten).flatten(1) %> +<% added = default_values - ordered_values %> +<% custom_fields_values = [ordered_values[0 ... cf_ids.first.count], + ordered_values[cf_ids.first.count .. -1] + added] %> +<% ids_of_nil.each_index{|i| ids_of_nil[i].each{|v| custom_fields_values[i].insert(v,nil)}} %> +<% else %> +<% custom_fields_values = [default_values[0 ... default_values.count/2], default_values[default_values.count/2 .. -1]] %> +<% end %> + +<%= render partial: "custom_fields_layouts/custom_fields_layout" , locals: {issue: @issue, custom_fields_values: custom_fields_values} %> + + Index: config/routes.rb =================================================================== --- config/routes.rb +++ config/routes.rb @@ -136,6 +136,9 @@ get 'versions.:format', :to => 'versions#index' get 'roadmap', :to => 'versions#index', :format => false get 'versions', :to => 'versions#index' + + resources :custom_fields_layouts, :only => [:show] + match 'settings/custom_fields_layouts', :to => 'custom_fields_layouts#apply', :via => [:post] resources :news, :except => [:show, :edit, :update, :destroy] resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do Index: app/helpers/issues_helper.rb =================================================================== --- app/helpers/issues_helper.rb +++ app/helpers/issues_helper.rb @@ -220,17 +220,57 @@ r.to_html end + #add empty elements for proper cfs showing + def add_nills(cfs) + diff = cfs[0].count - cfs[1].count + if (diff > 0) + cfs[1] += [nil] * diff + elsif (diff < 0) + cfs[0] += [nil] * (-diff) + end + cfs + end + def render_custom_fields_rows(issue) - values = issue.visible_custom_field_values - return if values.empty? - half = (values.size / 2.0).ceil - issue_fields_rows do |rows| - values.each_with_index do |value, i| - css = "cf_#{value.custom_field.id}" - m = (i < half ? :left : :right) - rows.send m, custom_field_name_tag(value.custom_field), show_value(value), :class => css + default_values = issue.visible_custom_field_values + return if default_values.empty? + + #get last edited custom field location + cf_layout = CustomFieldLayout.find_by(:tracker_id => issue.tracker_id, :project_id => issue.project_id) + cf_ids = cf_layout.nil? ? [[],[]] : JSON.parse(cf_layout.location.gsub("nil","null")) + + #get ids of nil elements + ids_of_nil = [[],[]] + cf_ids.each_index{ |i| cf_ids[i].each_index.select{|k| ids_of_nil[i] << k if cf_ids[i][k].nil?}}.map!{|i| i -= [nil]} + + #if not set yet then default + if !cf_ids.all?{|array| array.empty?} + ordered_values = default_values.group_by(&:custom_field_id).values_at(*cf_ids.flatten).flatten(1) + added = default_values - ordered_values + custom_fields_values = [ordered_values[0 ... cf_ids.first.count], + ordered_values[cf_ids.first.count .. -1] + added] + ids_of_nil.each_index{|i| ids_of_nil[i].each{|v| custom_fields_values[i].insert(v,nil)}} + else + custom_fields_values = [default_values[0 ... default_values.count/2], default_values[default_values.count/2 .. -1]] + end + custom_fields_values = add_nills(custom_fields_values) + cfs = custom_fields_values[0].zip(custom_fields_values[1]).flatten + + s = "\n" + n = 0 + cfs.each do |value| + s << "\n\n" if n > 0 && (n % 2) == 0 + if value.nil? + css = "custom_field_p clean" + s << "\t     \n" + else + css = "cf_#{value.custom_field_id}" + s << "\t#{ h(value.custom_field.name) }:#{ h(show_value(value)) }\n" end + n +=1 end + s << "\n" + s.html_safe end # Returns the path for updating the issue form Index: config/locales/ru.yml =================================================================== --- config/locales/ru.yml +++ config/locales/ru.yml @@ -469,6 +469,7 @@ label_current_version: Текущая версия label_custom_field: Настраиваемое поле label_custom_field_new: Новое настраиваемое поле + label_custom_field_layout: Редактирование расположения кастомных полей label_custom_field_plural: Настраиваемые поля label_date_from: С label_date_from_to: С %{start} по %{end} @@ -577,6 +578,7 @@ label_month: Месяц label_more_than_ago: более, чем дней(я) назад label_more: Больше + label_move_field: Переместите пустое поле label_my_account: Моя учётная запись label_my_page: Моя страница label_my_page_block: Блок моей страницы @@ -625,6 +627,7 @@ label_project_plural2: проекта label_project_plural5: проектов label_public_projects: Общие проекты + label_remove_field: Удалите пустое поле, перреместив его сюда label_query: Сохранённый запрос label_query_new: Новый запрос label_query_plural: Сохранённые запросы Index: config/locales/en.yml =================================================================== --- config/locales/en.yml (revision 1941) +++ config/locales/en.yml (revision 1956) @@ -551,6 +551,7 @@ label_custom_field: Custom field label_custom_field_plural: Custom fields label_custom_field_new: New custom field + label_custom_field_layout: Edit custom fields layout label_enumerations: Enumerations label_enumeration_new: New value label_information: Information @@ -560,6 +561,7 @@ label_login_with_open_id_option: or login with OpenID label_password_lost: Lost password label_home: Home + label_move_field: Move blank field label_my_page: My page label_my_account: My account label_my_projects: My projects @@ -572,6 +574,7 @@ label_assigned_to_me_issues: Issues assigned to me label_last_login: Last connection label_registered_on: Registered on + label_remove_field: Remove blank fields moving them here label_activity: Activity label_overall_activity: Overall activity label_user_activity: "%{value}'s activity"