]> source.dussan.org Git - redmine.git/commitdiff
Last updated by colum in issue list (#6375).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 5 Mar 2017 07:42:52 +0000 (07:42 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 5 Mar 2017 07:42:52 +0000 (07:42 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@16366 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/models/issue.rb
app/models/issue_query.rb
public/stylesheets/application.css
test/functional/issues_controller_test.rb
test/unit/query_test.rb

index 1877ca2c995266905677c5ced6616a56a7635ebb..19fef95b55f51cfde6ae88681fa5522f8ce75eb9 100644 (file)
@@ -245,6 +245,7 @@ class Issue < ActiveRecord::Base
     @spent_hours = nil
     @total_spent_hours = nil
     @total_estimated_hours = nil
+    @last_updated_by = nil
     base_reload(*args)
   end
 
@@ -1069,6 +1070,14 @@ class Issue < ActiveRecord::Base
     @relations ||= IssueRelation::Relations.new(self, (relations_from + relations_to).sort)
   end
 
+  def last_updated_by
+    if @last_updated_by
+      @last_updated_by.presence
+    else
+      journals.reorder(:id => :desc).first.try(:user)
+    end
+  end
+
   # Preloads relations for a collection of issues
   def self.load_relations(issues)
     if issues.any?
@@ -1132,6 +1141,23 @@ class Issue < ActiveRecord::Base
       where(:ancestors => {:id => issues.map(&:id)})
   end
 
+  # Preloads users who updated last a collection of issues
+  def self.load_visible_last_updated_by(issues, user=User.current)
+     if issues.any?
+      issue_ids = issues.map(&:id)
+      journals = Journal.joins(issue: :project).preload(:user).
+        where(:journalized_type => 'Issue', :journalized_id => issue_ids).
+        where("#{Journal.table_name}.id = (SELECT MAX(j.id) FROM #{Journal.table_name} j" +
+              " WHERE j.journalized_type='Issue' AND j.journalized_id=#{Journal.table_name}.journalized_id" +
+              " AND #{Journal.visible_notes_condition(user, :skip_pre_condition => true)})").to_a
+
+      issues.each do |issue|
+        journal = journals.detect {|j| j.journalized_id == issue.id}
+        issue.instance_variable_set("@last_updated_by", journal.try(:user) || '')
+      end
+    end
+  end
+
   # Finds an issue relation given its id.
   def find_relation(relation_id)
     IssueRelation.where("issue_to_id = ? OR issue_from_id = ?", id, id).find(relation_id)
index 458eea00a0b97edbd53ca574ab298b021bfb2e7c..88b51f5937cda1636deaf2e0c2936558e19ef8a6 100644 (file)
@@ -43,6 +43,7 @@ class IssueQuery < Query
     QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
     QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
     QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'),
+    QueryColumn.new(:last_updated_by, :sortable => lambda {User.fields_for_order_statement("last_journal_user")}),
     QueryColumn.new(:relations, :caption => :label_related_issues),
     QueryColumn.new(:description, :inline => false)
   ]
@@ -298,6 +299,9 @@ class IssueQuery < Query
     if has_column?(:total_spent_hours)
       Issue.load_visible_total_spent_hours(issues)
     end
+    if has_column?(:last_updated_by)
+      Issue.load_visible_last_updated_by(issues)
+    end
     if has_column?(:relations)
       Issue.load_visible_relations(issues)
     end
@@ -572,6 +576,11 @@ class IssueQuery < Query
       if order_options.include?('users')
         joins << "LEFT OUTER JOIN #{User.table_name} ON #{User.table_name}.id = #{queried_table_name}.assigned_to_id"
       end
+      if order_options.include?('last_journal_user')
+        joins << "LEFT OUTER JOIN #{Journal.table_name} ON #{Journal.table_name}.id = (SELECT MAX(#{Journal.table_name}.id) FROM #{Journal.table_name}" +
+                " WHERE #{Journal.table_name}.journalized_type='Issue' AND #{Journal.table_name}.journalized_id=#{Issue.table_name}.id AND #{Journal.visible_notes_condition(User.current, :skip_pre_condition => true)})" +
+                " LEFT OUTER JOIN #{User.table_name} last_journal_user ON last_journal_user.id = #{Journal.table_name}.user_id";
+      end
       if order_options.include?('versions')
         joins << "LEFT OUTER JOIN #{Version.table_name} ON #{Version.table_name}.id = #{queried_table_name}.fixed_version_id"
       end
index 1957a0d7f18f3ca6209e52b9cfedbf9daa3c0ca0..6c2ffed30c13bad5c3626e736bca4ebaffaf03d0 100644 (file)
@@ -254,7 +254,7 @@ tr.project.idnt-8 td.name {padding-left: 11em;}
 tr.project.idnt-9 td.name {padding-left: 12.5em;}
 
 tr.issue { text-align: center; white-space: nowrap; }
-tr.issue td.subject, tr.issue td.category, td.assigned_to, tr.issue td.string, tr.issue td.text, tr.issue td.list, tr.issue td.relations, tr.issue td.parent { white-space: normal; }
+tr.issue td.subject, tr.issue td.category, td.assigned_to, td.last_updated_by, tr.issue td.string, tr.issue td.text, tr.issue td.list, tr.issue td.relations, tr.issue td.parent { white-space: normal; }
 tr.issue td.relations { text-align: left; }
 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
 tr.issue td.relations span {white-space: nowrap;}
index b47ff53fbd35faa0ccc54e732cde26e68670fd08..6aa6a06232c92142bd9c89724c44c13a1a1be4c7 100644 (file)
@@ -741,6 +741,18 @@ class IssuesControllerTest < Redmine::ControllerTest
     assert_response :success
   end
 
+  def test_index_sort_by_last_updated_by
+    get :index, :sort => 'last_updated_by'
+    assert_response :success
+    assert_select 'table.issues.sort-by-last-updated-by.sort-asc'
+  end
+
+  def test_index_sort_by_last_updated_by_desc
+    get :index, :sort => 'last_updated_by:desc'
+    assert_response :success
+    assert_select 'table.issues.sort-by-last-updated-by.sort-desc'
+  end
+
   def test_index_sort_by_spent_hours
     get :index, :sort => 'spent_hours:desc'
     assert_response :success
@@ -970,6 +982,13 @@ class IssuesControllerTest < Redmine::ControllerTest
     assert_select 'td.parent a[title=?]', parent.subject
   end
 
+  def test_index_with_last_updated_by_column
+    get :index, :c => %w(subject last_updated_by), :issue_id => '1,2,3', :sort => 'id', :set_filter => '1'
+
+    assert_select 'td.last_updated_by'
+    assert_equal ["John Smith", "John Smith", ""], css_select('td.last_updated_by').map(&:text)
+  end
+
   def test_index_with_estimated_hours_total
     Issue.delete_all
     Issue.generate!(:estimated_hours => 5.5)
index 31a4ac2d3bac3f2c690828a30134bd33fd81cf71..77340e1ca86ef063ccb4bcd08cae418dcf036d39 100644 (file)
@@ -29,7 +29,7 @@ class QueryTest < ActiveSupport::TestCase
            :queries,
            :projects_trackers,
            :custom_fields_trackers,
-           :workflows,
+           :workflows, :journals,
            :attachments
 
   INTEGER_KLASS = RUBY_VERSION >= "2.4" ? Integer : Fixnum
@@ -762,7 +762,7 @@ class QueryTest < ActiveSupport::TestCase
       query = IssueQuery.new(:name => '_')
       filter_name = "updated_by"
       assert_include filter_name, query.available_filters.keys
-  
+
       query.filters = {filter_name => {:operator => '=', :values => ['me']}}
       assert_equal [2], find_issues_with_query(query).map(&:id).sort
     end
@@ -1322,6 +1322,19 @@ class QueryTest < ActiveSupport::TestCase
     assert_not_nil issues.first.instance_variable_get("@spent_hours")
   end
 
+  def test_query_should_preload_last_updated_by
+    with_current_user User.find(2) do
+      q = IssueQuery.new(:name => '_', :column_names => [:subject, :last_updated_by])
+      q.filters = {"issue_id" => {:operator => '=', :values => ['1,2,3']}}
+      assert q.has_column?(:last_updated_by)
+
+      issues = q.issues.sort_by(&:id)
+      assert issues.all? {|issue| !issue.instance_variable_get("@last_updated_by").nil?}
+      assert_equal ["User", "User", "NilClass"], issues.map { |i| i.last_updated_by.class.name}
+      assert_equal ["John Smith", "John Smith", ""], issues.map { |i| i.last_updated_by.to_s }
+    end
+  end
+
   def test_groupable_columns_should_include_custom_fields
     q = IssueQuery.new
     column = q.groupable_columns.detect {|c| c.name == :cf_1}
@@ -1384,6 +1397,16 @@ class QueryTest < ActiveSupport::TestCase
     end
   end
 
+  def test_sortable_columns_should_sort_last_updated_by_according_to_user_format_setting
+    with_settings :user_format => 'lastname_comma_firstname' do
+      q = IssueQuery.new
+      q.sort_criteria = [['last_updated_by', 'desc']]
+
+      assert q.sortable_columns.has_key?('last_updated_by')
+      assert_equal %w(last_journal_user.lastname last_journal_user.firstname last_journal_user.id), q.sortable_columns['last_updated_by']
+    end
+  end
+
   def test_sortable_columns_should_include_custom_field
     q = IssueQuery.new
     assert q.sortable_columns['cf_1']