summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/admin_controller.rb5
-rw-r--r--app/controllers/projects_controller.rb15
-rw-r--r--app/models/project.rb20
-rw-r--r--app/models/project_query.rb17
-rw-r--r--config/locales/en.yml1
-rw-r--r--config/locales/pt-BR.yml1
-rw-r--r--lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb9
-rw-r--r--lib/redmine/activity/fetcher.rb11
-rw-r--r--test/functional/projects_controller_test.rb12
-rw-r--r--test/unit/project_test.rb7
10 files changed, 79 insertions, 19 deletions
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index 7fcb6ac5d..7e36d5293 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -38,11 +38,10 @@ class AdminController < ApplicationController
def projects
retrieve_query(ProjectQuery, false, :defaults => @default_columns_names)
@query.admin_projects = 1
- scope = @query.results_scope
- @entry_count = scope.count
+ @entry_count = @query.result_count
@entry_pages = Paginator.new @entry_count, per_page_option, params['page']
- @projects = scope.limit(@entry_pages.per_page).offset(@entry_pages.offset).to_a
+ @projects = @query.results_scope(:limit => @entry_pages.per_page, :offset => @entry_pages.offset).to_a
render :action => "projects", :layout => false if request.xhr?
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 66d54db38..91616d619 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -53,31 +53,30 @@ class ProjectsController < ApplicationController
retrieve_default_query
retrieve_project_query
- scope = project_scope
respond_to do |format|
format.html do
# TODO: see what to do with the board view and pagination
if @query.display_type == 'board'
- @entries = scope.to_a
+ @entries = project_scope.to_a
else
- @entry_count = scope.count
+ @entry_count = @query.result_count
@entry_pages = Paginator.new @entry_count, per_page_option, params['page']
- @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).to_a
+ @entries = project_scope(:offset => @entry_pages.offset, :limit => @entry_pages.per_page).to_a
end
end
format.api do
@offset, @limit = api_offset_and_limit
- @project_count = scope.count
- @projects = scope.offset(@offset).limit(@limit).to_a
+ @project_count = @query.result_count
+ @projects = project_scope(:offset => @offset, :limit => @limit)
end
format.atom do
- projects = scope.reorder(:created_on => :desc).limit(Setting.feeds_limit.to_i).to_a
+ projects = project_scope(:order => {:created_on => :desc}, :limit => Setting.feeds_limit.to_i).to_a
render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
end
format.csv do
# Export all entries
- entries = scope.to_a
+ entries = project_scope.to_a
send_data(query_to_csv(entries, @query, params), :type => 'text/csv; header=present', :filename => 'projects.csv')
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index f9000829b..abac2a1f0 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -379,6 +379,7 @@ class Project < ApplicationRecord
@due_date = nil
@override_members = nil
@assignable_users = nil
+ @last_activity_date = nil
base_reload(*args)
end
@@ -1005,6 +1006,20 @@ class Project < ApplicationRecord
end
end
+ def last_activity_date
+ @last_activity_date || fetch_last_activity_date
+ end
+
+ # Preloads last activity date for a collection of projects
+ def self.load_last_activity_date(projects, user=User.current)
+ if projects.any?
+ last_activities = Redmine::Activity::Fetcher.new(User.current).events(nil, nil, :last_by_project => true).to_h
+ projects.each do |project|
+ project.instance_variable_set(:@last_activity_date, last_activities[project.id])
+ end
+ end
+ end
+
private
def update_inherited_members
@@ -1312,4 +1327,9 @@ class Project < ApplicationRecord
end
update_attribute :status, STATUS_ARCHIVED
end
+
+ def fetch_last_activity_date
+ latest_activities = Redmine::Activity::Fetcher.new(User.current, :project => self).events(nil, nil, :last_by_project => true)
+ latest_activities.empty? ? nil : latest_activities.to_h[self.id]
+ end
end
diff --git a/app/models/project_query.rb b/app/models/project_query.rb
index 113287ed8..dde3e9cc1 100644
--- a/app/models/project_query.rb
+++ b/app/models/project_query.rb
@@ -36,7 +36,8 @@ class ProjectQuery < Query
QueryColumn.new(:identifier, :sortable => "#{Project.table_name}.identifier"),
QueryColumn.new(:parent_id, :sortable => "#{Project.table_name}.lft ASC", :default_order => 'desc', :caption => :field_parent),
QueryColumn.new(:is_public, :sortable => "#{Project.table_name}.is_public", :groupable => true),
- QueryColumn.new(:created_on, :sortable => "#{Project.table_name}.created_on", :default_order => 'desc')
+ QueryColumn.new(:created_on, :sortable => "#{Project.table_name}.created_on", :default_order => 'desc'),
+ QueryColumn.new(:last_activity_date)
]
def self.default(project: nil, user: User.current)
@@ -140,6 +141,13 @@ class ProjectQuery < Query
end
end
+ # Returns the project count
+ def result_count
+ base_scope.count
+ rescue ::ActiveRecord::StatementInvalid => e
+ raise StatementInvalid, e.message
+ end
+
def results_scope(options={})
order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?)
@@ -156,6 +164,11 @@ class ProjectQuery < Query
scope = scope.preload(:parent)
end
- scope
+ projects = scope.to_a
+ if has_column?(:last_activity_date)
+ Project.load_last_activity_date(scope)
+ end
+
+ projects
end
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 3ae06f745..c842e4d7b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -419,6 +419,7 @@ en:
field_default_time_entry_activity: Default spent time activity
field_any_searchable: Any searchable text
field_estimated_remaining_hours: Estimated remaining time
+ field_last_activity_date: Last activity
setting_app_title: Application title
setting_welcome_text: Welcome text
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 93d5fad01..0a422ce86 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -299,6 +299,7 @@ pt-BR:
field_default_value: Padrão
field_comments_sorting: Visualizar comentários
field_parent_title: Página pai
+ field_last_activity_date: Última atividade
setting_app_title: Título da aplicação
setting_welcome_text: Texto de boas-vindas
diff --git a/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb b/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb
index 5954d501d..ac334c19a 100644
--- a/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb
+++ b/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb
@@ -64,9 +64,8 @@ module Redmine
ActiveSupport::Deprecation.warn "acts_as_activity_provider with implicit :scope option is deprecated. Please pass a scope on the #{self.name} as a proc."
end
- if from && to
- scope = scope.where("#{provider_options[:timestamp]} BETWEEN ? AND ?", from, to)
- end
+ scope = scope.where("#{provider_options[:timestamp]} >= ?", from) if from
+ scope = scope.where("#{provider_options[:timestamp]} <= ?", to) if to
if options[:author]
return [] if provider_options[:author_key].nil?
@@ -87,6 +86,10 @@ module Redmine
scope = scope.where(Project.allowed_to_condition(user, "view_#{self.name.underscore.pluralize}".to_sym, options))
end
+ if options[:last_by_project]
+ scope = scope.group("#{Project.table_name}.id").maximum(provider_options[:timestamp])
+ end
+
scope.to_a
end
end
diff --git a/lib/redmine/activity/fetcher.rb b/lib/redmine/activity/fetcher.rb
index cbd20425d..4264c2e5d 100644
--- a/lib/redmine/activity/fetcher.rb
+++ b/lib/redmine/activity/fetcher.rb
@@ -87,6 +87,7 @@ module Redmine
def events(from = nil, to = nil, options={})
e = []
@options[:limit] = options[:limit]
+ @options[:last_by_project] = options[:last_by_project] if options[:last_by_project]
@scope.each do |event_type|
constantized_providers(event_type).each do |provider|
@@ -94,10 +95,14 @@ module Redmine
end
end
- e.sort! {|a, b| b.event_datetime <=> a.event_datetime}
+ if options[:last_by_project]
+ e.sort!
+ else
+ e.sort! {|a, b| b.event_datetime <=> a.event_datetime}
- if options[:limit]
- e = e.slice(0, options[:limit])
+ if options[:limit]
+ e = e.slice(0, options[:limit])
+ end
end
e
end
diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb
index a2356185b..d1de3b631 100644
--- a/test/functional/projects_controller_test.rb
+++ b/test/functional/projects_controller_test.rb
@@ -252,6 +252,18 @@ class ProjectsControllerTest < Redmine::ControllerTest
assert_select ".total-for-cf-#{field.id} span.value", :text => '9'
end
+ def test_index_with_last_activity_date_column
+ with_settings :project_list_defaults => {'column_names' => %w(name short_description last_activity_date)} do
+ get :index, :params => {
+ :display_type => 'list'
+ }
+ assert_response :success
+ end
+ assert_equal ['Name', 'Description', 'Last activity'], columns_in_list
+ assert_select 'tr#project-1 td.last_activity_date', :text => format_time(Journal.find(3).created_on)
+ assert_select 'tr#project-4 td.last_activity_date', :text => ''
+ end
+
def test_index_should_retrieve_default_query
query = ProjectQuery.find(11)
ProjectQuery.stubs(:default).returns query
diff --git a/test/unit/project_test.rb b/test/unit/project_test.rb
index eacbf0c92..a7047d71c 100644
--- a/test/unit/project_test.rb
+++ b/test/unit/project_test.rb
@@ -1161,4 +1161,11 @@ class ProjectTest < ActiveSupport::TestCase
r = Project.like('eco_k')
assert_include project, r
end
+
+ def test_last_activity_date
+ # Note with id 3 is the last activity on Project 1
+ assert_equal Journal.find(3).created_on, Project.find(1).last_activity_date
+ # Project without activity should return nil
+ assert_nil Project.find(4).last_activity_date
+ end
end