Source: redmine
Version: 3.3.1.-2
Severity: important
Tags: patch

Dear Maintainer,

we found a bug in the upcomming Redmine version in Debian/testing:
Changing the status of an issue (or several issues) using bulk edit (right
click in "Issues" view) leads to a unresovable situation if the new status
defines required fields which where read-only in the former status.

Steps to reproduce the problem:

   * create a tracker defining a workflow with read-only fields which become
required fields while a status change
   * create an issue or several tickets for this tracker
   * set all created tickets on the status which renders some fields as read-
only
   * perform a query ("Issues") which shows the issues as result list
   * right-click on a ticket or several tickets and change status using the
pop-up menu

The result is a view of the ticket with the message "following fields must be
filled out...", naming the required fields for the new status. However, the
required fields are not visible. The issue cannot be changed. Made changes
cannot be saved and are lost.

The same problem occurs when using the "Edit..." option to perform a bulk edit
operation (right click, pop-up menu->Edit)

We would expect to be able to perform the status change and edit the required
fields to save the changes in the issue.

The bug is known to the Redmine team and documented here:
http://www.redmine.org/issues/23755
Version Redmine version 3.4 will fix this problem.

Basing on the commit which is refered to in the bugreport, I created a patch
for Redmine 3.3. Please consider applying it to the upcomming redmine packages.

Regading my system information: I tested the bugfix in a lxc container using
the current Debian/testing (Stretch).



-- System Information:
Debian Release: 8.7
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 3.16.0-4-amd64 (SMP w/8 CPU cores)
Locale: LANG=de_DE.UTF-8, LC_CTYPE=de_DE.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)

*** /media/transfer/klose/bulk-update.patch
diff -ruN app.old/controllers/issues_controller.rb
app/controllers/issues_controller.rb
--- app.old/controllers/issues_controller.rb    2016-10-10 07:48:13.000000000
+0000
+++ app/controllers/issues_controller.rb        2017-01-25 12:48:48.682492410
+0000
@@ -217,24 +217,41 @@
       end
     end

+    edited_issues = Issue.where(:id => @issues.map(&:id)).to_a
+
     @allowed_projects = Issue.allowed_target_projects
     if params[:issue]
       @target_project = @allowed_projects.detect {|p| p.id.to_s ==
params[:issue][:project_id].to_s}
       if @target_project
         target_projects = [@target_project]
+        edited_issues.each {|issue| issue.project = @target_project}
       end
     end
     target_projects ||= @projects

+    @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p)
}.reduce(:&)
+    if params[:issue]
+      @target_tracker = @trackers.detect {|t| t.id.to_s ==
params[:issue][:tracker_id].to_s}
+      if @target_tracker
+        edited_issues.each {|issue| issue.tracker = @target_tracker}
+      end
+    end
+
     if @copy
       # Copied issues will get their default statuses
       @available_statuses = []
     else
-      @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
+      @available_statuses =
edited_issues.map(&:new_statuses_allowed_to).reduce(:&)
     end
-    @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&)
+    if params[:issue]
+      @target_status = @available_statuses.detect {|t| t.id.to_s ==
params[:issue][:status_id].to_s}
+      if @target_status
+        edited_issues.each {|issue| issue.status = @target_status}
+      end
+    end
+
+    @custom_fields = edited_issues.map{|i|i.editable_custom_fields}.reduce(:&)
     @assignables = target_projects.map(&:assignable_users).reduce(:&)
-    @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p)
}.reduce(:&)
     @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
     @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
     if @copy
@@ -242,7 +259,7 @@
       @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
     end

-    @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
+    @safe_attributes = edited_issues.map(&:safe_attribute_names).reduce(:&)

     @issue_params = params[:issue] || {}
     @issue_params[:custom_field_values] ||= {}
diff -ruN app.old/views/issues/bulk_edit.html.erb
app/views/issues/bulk_edit.html.erb
--- app.old/views/issues/bulk_edit.html.erb     2016-10-10 07:48:13.000000000
+0000
+++ app/views/issues/bulk_edit.html.erb 2017-01-25 12:48:46.734436014 +0000
@@ -43,14 +43,16 @@
   <label for="issue_tracker_id"><%= l(:field_tracker) %></label>
   <%= select_tag('issue[tracker_id]',
                  content_tag('option', l(:label_no_change_option), :value =>
'') +
-                   options_from_collection_for_select(@trackers, :id, :name,
@issue_params[:tracker_id])) %>
+                   options_from_collection_for_select(@trackers, :id, :name,
@issue_params[:tracker_id]),
+                   :onchange => "updateBulkEditFrom('#{escape_javascript
url_for(:action => 'bulk_edit', :format => 'js')}')") %>
 </p>
 <% if @available_statuses.any? %>
 <p>
   <label for='issue_status_id'><%= l(:field_status) %></label>
   <%= select_tag('issue[status_id]',
                  content_tag('option', l(:label_no_change_option), :value =>
'') +
-                   options_from_collection_for_select(@available_statuses,
:id, :name, @issue_params[:status_id])) %>
+                   options_from_collection_for_select(@available_statuses,
:id, :name, @issue_params[:status_id]),
+                   :onchange => "updateBulkEditFrom('#{escape_javascript
url_for(:action => 'bulk_edit', :format => 'js')}')") %>
 </p>
diff -ruN app.old/controllers/issues_controller.rb app/controllers/issues_controller.rb
--- app.old/controllers/issues_controller.rb	2016-10-10 07:48:13.000000000 +0000
+++ app/controllers/issues_controller.rb	2017-01-25 12:48:48.682492410 +0000
@@ -217,24 +217,41 @@
       end
     end
 
+    edited_issues = Issue.where(:id => @issues.map(&:id)).to_a
+
     @allowed_projects = Issue.allowed_target_projects
     if params[:issue]
       @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
       if @target_project
         target_projects = [@target_project]
+        edited_issues.each {|issue| issue.project = @target_project}
       end
     end
     target_projects ||= @projects
 
+    @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&)
+    if params[:issue]
+      @target_tracker = @trackers.detect {|t| t.id.to_s == params[:issue][:tracker_id].to_s}
+      if @target_tracker
+        edited_issues.each {|issue| issue.tracker = @target_tracker}
+      end
+    end
+
     if @copy
       # Copied issues will get their default statuses
       @available_statuses = []
     else
-      @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
+      @available_statuses = edited_issues.map(&:new_statuses_allowed_to).reduce(:&)
     end
-    @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&)
+    if params[:issue]
+      @target_status = @available_statuses.detect {|t| t.id.to_s == params[:issue][:status_id].to_s}
+      if @target_status
+        edited_issues.each {|issue| issue.status = @target_status}
+      end
+    end
+
+    @custom_fields = edited_issues.map{|i|i.editable_custom_fields}.reduce(:&)
     @assignables = target_projects.map(&:assignable_users).reduce(:&)
-    @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&)
     @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
     @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
     if @copy
@@ -242,7 +259,7 @@
       @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
     end
 
-    @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
+    @safe_attributes = edited_issues.map(&:safe_attribute_names).reduce(:&)
 
     @issue_params = params[:issue] || {}
     @issue_params[:custom_field_values] ||= {}
diff -ruN app.old/views/issues/bulk_edit.html.erb app/views/issues/bulk_edit.html.erb
--- app.old/views/issues/bulk_edit.html.erb	2016-10-10 07:48:13.000000000 +0000
+++ app/views/issues/bulk_edit.html.erb	2017-01-25 12:48:46.734436014 +0000
@@ -43,14 +43,16 @@
   <label for="issue_tracker_id"><%= l(:field_tracker) %></label>
   <%= select_tag('issue[tracker_id]',
                  content_tag('option', l(:label_no_change_option), :value => '') +
-                   options_from_collection_for_select(@trackers, :id, :name, @issue_params[:tracker_id])) %>
+                   options_from_collection_for_select(@trackers, :id, :name, @issue_params[:tracker_id]),
+                   :onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %>
 </p>
 <% if @available_statuses.any? %>
 <p>
   <label for='issue_status_id'><%= l(:field_status) %></label>
   <%= select_tag('issue[status_id]',
                  content_tag('option', l(:label_no_change_option), :value => '') +
-                   options_from_collection_for_select(@available_statuses, :id, :name, @issue_params[:status_id])) %>
+                   options_from_collection_for_select(@available_statuses, :id, :name, @issue_params[:status_id]),
+                   :onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %>
 </p>
 <% end %>
 

Reply via email to