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"