diff --git a/app/controllers/.versions_controller.rb.swp b/app/controllers/.versions_controller.rb.swp new file mode 100644 index 0000000..bfaef6e Binary files /dev/null and b/app/controllers/.versions_controller.rb.swp differ diff --git a/app/controllers/enumerations_controller.rb b/app/controllers/enumerations_controller.rb index a3d4e47..d09cd38 100644 --- a/app/controllers/enumerations_controller.rb +++ b/app/controllers/enumerations_controller.rb @@ -25,7 +25,7 @@ class EnumerationsController < ApplicationController before_action :require_admin_or_api_request, :only => :index before_action :build_new_enumeration, :only => [:new, :create] before_action :find_enumeration, :only => [:edit, :update, :destroy] - accept_api_auth :index + accept_api_auth :index, :update helper :custom_fields diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 65caee6..10d2784 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -184,11 +184,13 @@ class IssuesController < ApplicationController respond_to do |format| format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) } + format.js { head 200 } format.api { render_api_ok } end else respond_to do |format| format.html { render :action => 'edit' } + format.js { head 422 } format.api { render_validation_errors(@issue) } end end diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index 15c24a8..18b2027 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -52,7 +52,8 @@ class VersionsController < ApplicationController includes(:project, :tracker). preload(:status, :priority, :fixed_version). where(:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)). - order("#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id") + #order("#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id") + order(order_issues_by) @issues_by_version = issues.group_by(&:fixed_version) end @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?} @@ -69,7 +70,8 @@ class VersionsController < ApplicationController @issues = @version.fixed_issues.visible. includes(:status, :tracker, :priority). preload(:project). - reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id"). + #reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id"). + reorder(order_issues_by). to_a } format.api @@ -173,6 +175,14 @@ class VersionsController < ApplicationController end end + def order_issues_by + if Setting.manual_issue_position_in_versions == '1' + return "COALESCE(#{Issue.table_name}.position, 999999), #{Issue.table_name}.id" + else + return "#{Tracker.table_name}.position, #{Issue.table_name}.id" + end + end + private def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil) diff --git a/app/models/issue.rb b/app/models/issue.rb index efb55fa..27c50c0 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -54,6 +54,8 @@ class Issue < ActiveRecord::Base acts_as_activity_provider :scope => preload(:project, :author, :tracker, :status), :author_key => :author_id + acts_as_positioned :scope => [:fixed_version_id] + DONE_RATIO_OPTIONS = %w(issue_field issue_status) attr_writer :deleted_attachment_ids @@ -494,6 +496,10 @@ class Issue < ActiveRecord::Base (issue.new_record? || issue.attributes_editable?(user)) && user.allowed_to?(:manage_subtasks, issue.project) }) + safe_attributes( + 'position', + :if => lambda {|issue, user| user.allowed_to?(:change_issue_position_in_version, issue.project)} + ) safe_attributes( 'deleted_attachment_ids', :if => lambda {|issue, user| issue.attachments_deletable?(user)}) @@ -832,7 +838,7 @@ class Issue < ActiveRecord::Base # Returns the names of attributes that are journalized when updating the issue def journalized_attribute_names - names = Issue.column_names - %w(id root_id lft rgt lock_version created_on updated_on closed_on) + names = Issue.column_names - %w(id root_id lft rgt lock_version position created_on updated_on closed_on) if tracker names -= tracker.disabled_core_fields end diff --git a/app/views/settings/_issues.html.erb b/app/views/settings/_issues.html.erb index b4e50d8..e363c18 100644 --- a/app/views/settings/_issues.html.erb +++ b/app/views/settings/_issues.html.erb @@ -15,6 +15,8 @@

<%= setting_check_box :display_subprojects_issues %>

+

<%= setting_check_box :manual_issue_position_in_versions %>

+

<%= setting_select :issue_done_ratio, Issue::DONE_RATIO_OPTIONS.collect {|i| [l("setting_issue_done_ratio_#{i}"), i]} %>

<%= setting_multiselect :non_working_week_days, (1..7).map {|d| [day_name(d), d.to_s]}, :inline => true %>

diff --git a/app/views/versions/index.html.erb b/app/views/versions/index.html.erb index afe9b46..5d64d09 100644 --- a/app/views/versions/index.html.erb +++ b/app/views/versions/index.html.erb @@ -32,14 +32,19 @@ <%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%> - <% issues.each do |issue| -%> - - - - - - - <% end -%> + + <% issues.each do |issue| -%> + + + + + + <% if Setting.manual_issue_position_in_versions == '1' && User.current.allowed_to?(:change_issue_position_in_version, version.project) %> + + <% end%> + + <% end -%> + <% end %> <% end %> @@ -108,3 +113,9 @@ <% html_title(l(:label_roadmap)) %> <%= context_menu %> + +<% if Setting.manual_issue_position_in_versions == '1' && User.current.allowed_to?(:change_issue_position_in_version, @project) %> + <%= javascript_tag do %> + $(function() { $("table.related-issues tbody").positionedItems(); }); + <% end %> +<% end %> diff --git a/app/views/versions/show.html.erb b/app/views/versions/show.html.erb index d73485a..e9b3abb 100644 --- a/app/views/versions/show.html.erb +++ b/app/views/versions/show.html.erb @@ -41,15 +41,20 @@ <% if @issues.present? %> <%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%> - - <%- @issues.each do |issue| -%> - - - - - - - <% end %> + + + <%- @issues.each do |issue| -%> + + + + + + <% if Setting.manual_issue_position_in_versions == '1' && !@version.closed? && User.current.allowed_to?(:change_issue_position_in_version, @version.project) %> + + <% end%> + + <% end %> + <% end %> <%= context_menu %> @@ -59,3 +64,9 @@ <%= call_hook :view_versions_show_bottom, :version => @version %> <% html_title @version.name %> + +<% if Setting.manual_issue_position_in_versions == '1' && !@version.closed? && User.current.allowed_to?(:change_issue_position_in_version, @version.project) %> + <%= javascript_tag do %> + $(function() { $("table.related-issues tbody").positionedItems(); }); + <% end %> +<% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 031c7ba..3e16e62 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -485,6 +485,7 @@ en: setting_timelog_accept_future_dates: Accept time logs on future dates setting_show_status_changes_in_mail_subject: Show status changes in issue mail notifications subject setting_project_list_defaults: Projects list defaults + setting_manual_issue_position_in_versions: Enable manually set issue position in versions permission_add_project: Create project permission_add_subprojects: Create subprojects @@ -553,6 +554,7 @@ en: permission_manage_related_issues: Manage related issues permission_import_issues: Import issues permission_log_time_for_other_users: Log spent time for other users + permission_change_issue_position_in_version: Change issue position in version project_module_issue_tracking: Issue tracking project_module_time_tracking: Time tracking diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 9cd41b0..2867ae9 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -482,6 +482,7 @@ fr: setting_time_entry_list_defaults: Affichage par défaut de la liste des temps passés setting_timelog_accept_0_hours: Autoriser la saisie de temps avec 0 heure setting_timelog_max_hours_per_day: Maximum d'heures pouvant être saisies par un utilisateur sur un jour + setting_manual_issue_position_in_versions: Activer le positionnement manuel des tickets dans les versions permission_add_project: Créer un projet permission_add_subprojects: Créer des sous-projets @@ -548,6 +549,7 @@ fr: permission_manage_subtasks: Gérer les sous-tâches permission_manage_related_issues: Gérer les demandes associées permission_import_issues: Importer des demandes + permission_change_issue_position_in_version: Changer la position d'un ticket dans une version project_module_issue_tracking: Suivi des demandes project_module_time_tracking: Suivi du temps passé diff --git a/config/settings.yml b/config/settings.yml index b5fc647..973c01c 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -177,6 +177,8 @@ parent_issue_done_ratio: default: 'derived' link_copied_issue: default: 'ask' +manual_issue_position_in_versions: + default: 1 close_duplicate_issues: default: 1 issue_group_assignment: diff --git a/db/migrate/20200315154300_add_issue_position.rb b/db/migrate/20200315154300_add_issue_position.rb new file mode 100644 index 0000000..ee0abec --- /dev/null +++ b/db/migrate/20200315154300_add_issue_position.rb @@ -0,0 +1,9 @@ +class AddIssuePosition < ActiveRecord::Migration[5.2] + def self.up + add_column :issues, :position, :integer + end + + def self.down + remove_column :issues, :position + end +end diff --git a/lib/redmine.rb b/lib/redmine.rb index 9ecca63..a0773b6 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -116,6 +116,7 @@ Redmine::AccessControl.map do |map| map.permission :view_private_notes, {}, :read => true, :require => :member map.permission :set_notes_private, {}, :require => :member map.permission :delete_issues, {:issues => :destroy}, :require => :member + map.permission :change_issue_position_in_version, {} # Watchers map.permission :view_issue_watchers, {}, :read => true map.permission :add_issue_watchers, {:watchers => [:new, :create, :append, :autocomplete_for_user]} diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 203774a..4876af1 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -607,6 +607,7 @@ div#roadmap .related-issues { margin-bottom: 1em; } div#roadmap .related-issues td.checkbox { display: none; } div#roadmap .related-issues td.assigned_to { width:1px; white-space:nowrap; padding: 0; } div#roadmap .related-issues td.assigned_to img { padding-left: 4px; padding-right: 4px;} +div#roadmap .related-issues td.sortable { text-align: right; } div#roadmap .wiki h1:first-child { display: none; } div#roadmap .wiki h1 { font-size: 120%; } div#roadmap .wiki h2 { font-size: 110%; } diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb index c33caef..f292a27 100644 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -39,6 +39,7 @@ class IssueQuery < Query QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date", :groupable => true), QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date", :groupable => true), QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours", :totalable => true), + QueryColumn.new(:position, :sortable => "#{Issue.table_name}.position"), QueryColumn.new( :total_estimated_hours, :sortable => -> { @@ -157,6 +158,7 @@ class IssueQuery < Query add_available_filter "start_date", :type => :date add_available_filter "due_date", :type => :date add_available_filter "estimated_hours", :type => :float + add_available_filter "position", :type => :integer if User.current.allowed_to?(:view_time_entries, project, :global => true) add_available_filter "spent_time", :type => :float, :label => :label_spent_time