diff --git a/app/models/issue.rb b/app/models/issue.rb old mode 100644 new mode 100755 index 1877ca2..cb4c065 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -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,15 @@ 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 + else + journal = self.journals.preload(:user).visible.last + journal.user if journal + end + end + # Preloads relations for a collection of issues def self.load_relations(issues) if issues.any? @@ -1132,6 +1142,21 @@ 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) + 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.visible_notes_condition(User.current, :skip_pre_condition => true)).to_a + + issues.each do |issue| + journal = journals.select{|journal| journal.journalized_id == issue.id} + issue.instance_variable_set "@last_updated_by", (journal.empty? ? '' : journal.last.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) diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb old mode 100644 new mode 100755 index fd5c933..eb201fc --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -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) ] @@ -294,6 +295,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 @@ -556,6 +560,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 diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 641dd01..a1cf4e2 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -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;} diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb old mode 100644 new mode 100755 index 81f6251..5d1b4e9 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -759,7 +759,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 @@ -1287,6 +1287,21 @@ class QueryTest < ActiveSupport::TestCase assert_not_nil issues.first.instance_variable_get("@spent_hours") end + def test_query_should_preload_last_updated_by + q = IssueQuery.new(:name => '_', :column_names => [:subject, :last_updated_by]) + assert q.has_column?(:last_updated_by) + issues = q.issues + assert_not_nil issues.first.instance_variable_get("@last_updated_by") + end + + def test_query_with_last_updated_by_column + q = IssueQuery.new(:name => '_', :column_names => [:subject, :last_updated_by]) + q.filters = {"project_id" => {:operator => '=', :values => [1]}} + + assert_equal ["User", "String", "String", "User", "String", "String", "String"], q.issues.map { |i| i.last_updated_by.class.name} + assert_equal ["John Smith", "", "", "John Smith", "", "", ""], q.issues.map { |i| i.last_updated_by.to_s } + end + def test_groupable_columns_should_include_custom_fields q = IssueQuery.new column = q.groupable_columns.detect {|c| c.name == :cf_1} @@ -1349,6 +1364,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']