Display latest user's activity on account/show view.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2066 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
b5fcea9e74
commit
fce4615f10
|
@ -1,5 +1,5 @@
|
||||||
# redMine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
# Copyright (C) 2006-2008 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
@ -31,6 +31,10 @@ class AccountController < ApplicationController
|
||||||
@memberships = @user.memberships.select do |membership|
|
@memberships = @user.memberships.select do |membership|
|
||||||
membership.project.is_public? || (User.current.member_of?(membership.project))
|
membership.project.is_public? || (User.current.member_of?(membership.project))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
|
||||||
|
@events_by_day = events.group_by(&:event_date)
|
||||||
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render_404
|
render_404
|
||||||
end
|
end
|
||||||
|
|
|
@ -105,6 +105,18 @@ module ApplicationHelper
|
||||||
@time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
|
@time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
|
||||||
include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
|
include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_activity_title(text)
|
||||||
|
h(truncate_single_line(text, 100))
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_activity_day(date)
|
||||||
|
date == Date.today ? l(:label_today).titleize : format_date(date)
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_activity_description(text)
|
||||||
|
h(truncate(text.to_s, 250).gsub(%r{<(pre|code)>.*$}m, '...'))
|
||||||
|
end
|
||||||
|
|
||||||
def distance_of_date_in_words(from_date, to_date = 0)
|
def distance_of_date_in_words(from_date, to_date = 0)
|
||||||
from_date = from_date.to_date if from_date.respond_to?(:to_date)
|
from_date = from_date.to_date if from_date.respond_to?(:to_date)
|
||||||
|
|
|
@ -21,18 +21,6 @@ module ProjectsHelper
|
||||||
link_to h(version.name), { :controller => 'versions', :action => 'show', :id => version }, options
|
link_to h(version.name), { :controller => 'versions', :action => 'show', :id => version }, options
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_activity_title(text)
|
|
||||||
h(truncate_single_line(text, 100))
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_activity_day(date)
|
|
||||||
date == Date.today ? l(:label_today).titleize : format_date(date)
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_activity_description(text)
|
|
||||||
h(truncate(text.to_s, 250).gsub(%r{<(pre|code)>.*$}m, '...'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def project_settings_tabs
|
def project_settings_tabs
|
||||||
tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
|
tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
|
||||||
{:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
|
{:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
|
||||||
|
|
|
@ -30,12 +30,14 @@ class Attachment < ActiveRecord::Base
|
||||||
|
|
||||||
acts_as_activity_provider :type => 'files',
|
acts_as_activity_provider :type => 'files',
|
||||||
:permission => :view_files,
|
:permission => :view_files,
|
||||||
|
:author_key => :author_id,
|
||||||
:find_options => {:select => "#{Attachment.table_name}.*",
|
:find_options => {:select => "#{Attachment.table_name}.*",
|
||||||
:joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
|
:joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
|
||||||
"LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id"}
|
"LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id"}
|
||||||
|
|
||||||
acts_as_activity_provider :type => 'documents',
|
acts_as_activity_provider :type => 'documents',
|
||||||
:permission => :view_documents,
|
:permission => :view_documents,
|
||||||
|
:author_key => :author_id,
|
||||||
:find_options => {:select => "#{Attachment.table_name}.*",
|
:find_options => {:select => "#{Attachment.table_name}.*",
|
||||||
:joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
|
:joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
|
||||||
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
|
"LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
|
||||||
|
|
|
@ -34,6 +34,7 @@ class Changeset < ActiveRecord::Base
|
||||||
:date_column => 'committed_on'
|
:date_column => 'committed_on'
|
||||||
|
|
||||||
acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
|
acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
|
||||||
|
:author_key => :user_id,
|
||||||
:find_options => {:include => {:repository => :project}}
|
:find_options => {:include => {:repository => :project}}
|
||||||
|
|
||||||
validates_presence_of :repository_id, :revision, :committed_on, :commit_date
|
validates_presence_of :repository_id, :revision, :committed_on, :commit_date
|
||||||
|
|
|
@ -42,7 +42,8 @@ class Issue < ActiveRecord::Base
|
||||||
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"},
|
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"},
|
||||||
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}
|
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}
|
||||||
|
|
||||||
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]}
|
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
|
||||||
|
:author_key => :author_id
|
||||||
|
|
||||||
validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status
|
validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status
|
||||||
validates_length_of :subject, :maximum => 255
|
validates_length_of :subject, :maximum => 255
|
||||||
|
|
|
@ -33,6 +33,7 @@ class Journal < ActiveRecord::Base
|
||||||
|
|
||||||
acts_as_activity_provider :type => 'issues',
|
acts_as_activity_provider :type => 'issues',
|
||||||
:permission => :view_issues,
|
:permission => :view_issues,
|
||||||
|
:author_key => :user_id,
|
||||||
:find_options => {:include => [{:issue => :project}, :details, :user],
|
:find_options => {:include => [{:issue => :project}, :details, :user],
|
||||||
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
|
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
|
||||||
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
|
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
|
||||||
|
|
|
@ -32,7 +32,8 @@ class Message < ActiveRecord::Base
|
||||||
:url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
|
:url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
|
||||||
{:id => o.parent_id, :anchor => "message-#{o.id}"})}
|
{:id => o.parent_id, :anchor => "message-#{o.id}"})}
|
||||||
|
|
||||||
acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]}
|
acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]},
|
||||||
|
:author_key => :author_id
|
||||||
acts_as_watchable
|
acts_as_watchable
|
||||||
|
|
||||||
attr_protected :locked, :sticky
|
attr_protected :locked, :sticky
|
||||||
|
|
|
@ -26,7 +26,8 @@ class News < ActiveRecord::Base
|
||||||
|
|
||||||
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
|
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
|
||||||
acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
|
acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
|
||||||
acts_as_activity_provider :find_options => {:include => [:project, :author]}
|
acts_as_activity_provider :find_options => {:include => [:project, :author]},
|
||||||
|
:author_key => :author_id
|
||||||
|
|
||||||
# returns latest news for projects visible by user
|
# returns latest news for projects visible by user
|
||||||
def self.latest(user = User.current, count = 5)
|
def self.latest(user = User.current, count = 5)
|
||||||
|
|
|
@ -37,6 +37,7 @@ class WikiContent < ActiveRecord::Base
|
||||||
|
|
||||||
acts_as_activity_provider :type => 'wiki_edits',
|
acts_as_activity_provider :type => 'wiki_edits',
|
||||||
:timestamp => "#{WikiContent.versioned_table_name}.updated_on",
|
:timestamp => "#{WikiContent.versioned_table_name}.updated_on",
|
||||||
|
:author_key => "#{WikiContent.versioned_table_name}.author_id",
|
||||||
:permission => :view_wiki_edits,
|
:permission => :view_wiki_edits,
|
||||||
:find_options => {:select => "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
|
:find_options => {:select => "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
|
||||||
"#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
|
"#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
<h2><%= avatar @user %> <%=h @user.name %></h2>
|
<h2><%= avatar @user %> <%=h @user.name %></h2>
|
||||||
|
|
||||||
|
<div class="splitcontentleft">
|
||||||
<p>
|
<p>
|
||||||
<%= mail_to(h(@user.mail)) unless @user.pref.hide_mail %>
|
<%= mail_to(h(@user.mail)) unless @user.pref.hide_mail %>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -25,8 +26,32 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="splitcontentright">
|
||||||
|
|
||||||
|
<% unless @events_by_day.empty? %>
|
||||||
<h3><%=l(:label_activity)%></h3>
|
<h3><%=l(:label_activity)%></h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<%=l(:label_reported_issues)%>: <%= Issue.count(:conditions => ["author_id=?", @user.id]) %>
|
<%=l(:label_reported_issues)%>: <%= Issue.count(:conditions => ["author_id=?", @user.id]) %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div id="activity">
|
||||||
|
<% @events_by_day.keys.sort.reverse.each do |day| %>
|
||||||
|
<h4><%= format_activity_day(day) %></h4>
|
||||||
|
<dl>
|
||||||
|
<% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
|
||||||
|
<dt class="<%= e.event_type %>">
|
||||||
|
<span class="time"><%= format_time(e.event_datetime, false) %></span>
|
||||||
|
<%= content_tag('span', h(e.project), :class => 'project') %>
|
||||||
|
<%= link_to format_activity_title(e.event_title), e.event_url %></dt>
|
||||||
|
<dd><span class="description"><%= format_activity_description(e.event_description) %></span></dd>
|
||||||
|
<% end -%>
|
||||||
|
</dl>
|
||||||
|
<% end -%>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% html_title @user.name %>
|
||||||
|
|
|
@ -25,7 +25,7 @@ module Redmine
|
||||||
@@constantized_providers = Hash.new {|h,k| h[k] = Redmine::Activity.providers[k].collect {|t| t.constantize } }
|
@@constantized_providers = Hash.new {|h,k| h[k] = Redmine::Activity.providers[k].collect {|t| t.constantize } }
|
||||||
|
|
||||||
def initialize(user, options={})
|
def initialize(user, options={})
|
||||||
options.assert_valid_keys(:project, :with_subprojects)
|
options.assert_valid_keys(:project, :with_subprojects, :author)
|
||||||
@user = user
|
@user = user
|
||||||
@project = options[:project]
|
@project = options[:project]
|
||||||
@options = options
|
@options = options
|
||||||
|
@ -58,14 +58,20 @@ module Redmine
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an array of events for the given date range
|
# Returns an array of events for the given date range
|
||||||
def events(from, to)
|
def events(from = nil, to = nil, options={})
|
||||||
e = []
|
e = []
|
||||||
|
@options[:limit] = options[:limit]
|
||||||
|
|
||||||
@scope.each do |event_type|
|
@scope.each do |event_type|
|
||||||
constantized_providers(event_type).each do |provider|
|
constantized_providers(event_type).each do |provider|
|
||||||
e += provider.find_events(event_type, @user, from, to, @options)
|
e += provider.find_events(event_type, @user, from, to, @options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if options[:limit]
|
||||||
|
e.sort! {|a,b| b.event_date <=> a.event_date}
|
||||||
|
e = e.slice(0, options[:limit])
|
||||||
|
end
|
||||||
e
|
e
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,15 @@ class ActivityTest < Test::Unit::TestCase
|
||||||
assert events.include?(Issue.find(4))
|
assert events.include?(Issue.find(4))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_user_activity
|
||||||
|
user = User.find(2)
|
||||||
|
events = Redmine::Activity::Fetcher.new(User.anonymous, :author => user).events(nil, nil, :limit => 10)
|
||||||
|
|
||||||
|
assert(events.size > 0)
|
||||||
|
assert(events.size <= 10)
|
||||||
|
assert_nil(events.detect {|e| e.event_author != user})
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def find_events(user, options={})
|
def find_events(user, options={})
|
||||||
|
|
|
@ -29,7 +29,7 @@ module Redmine
|
||||||
send :include, Redmine::Acts::ActivityProvider::InstanceMethods
|
send :include, Redmine::Acts::ActivityProvider::InstanceMethods
|
||||||
end
|
end
|
||||||
|
|
||||||
options.assert_valid_keys(:type, :permission, :timestamp, :find_options)
|
options.assert_valid_keys(:type, :permission, :timestamp, :author_key, :find_options)
|
||||||
self.activity_provider_options ||= {}
|
self.activity_provider_options ||= {}
|
||||||
|
|
||||||
# One model can provide different event types
|
# One model can provide different event types
|
||||||
|
@ -39,6 +39,7 @@ module Redmine
|
||||||
options[:permission] = "view_#{self.name.underscore.pluralize}".to_sym unless options.has_key?(:permission)
|
options[:permission] = "view_#{self.name.underscore.pluralize}".to_sym unless options.has_key?(:permission)
|
||||||
options[:timestamp] ||= "#{table_name}.created_on"
|
options[:timestamp] ||= "#{table_name}.created_on"
|
||||||
options[:find_options] ||= {}
|
options[:find_options] ||= {}
|
||||||
|
options[:author_key] = "#{table_name}.#{options[:author_key]}" if options[:author_key].is_a?(Symbol)
|
||||||
self.activity_provider_options[event_type] = options
|
self.activity_provider_options[event_type] = options
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -54,10 +55,21 @@ module Redmine
|
||||||
provider_options = activity_provider_options[event_type]
|
provider_options = activity_provider_options[event_type]
|
||||||
raise "#{self.name} can not provide #{event_type} events." if provider_options.nil?
|
raise "#{self.name} can not provide #{event_type} events." if provider_options.nil?
|
||||||
|
|
||||||
cond = ARCondition.new(["#{provider_options[:timestamp]} BETWEEN ? AND ?", from, to])
|
scope_options = {}
|
||||||
|
cond = ARCondition.new
|
||||||
|
if from && to
|
||||||
|
cond.add(["#{provider_options[:timestamp]} BETWEEN ? AND ?", from, to])
|
||||||
|
end
|
||||||
|
if options[:author]
|
||||||
|
return [] if provider_options[:author_key].nil?
|
||||||
|
cond.add(["#{provider_options[:author_key]} = ?", options[:author].id])
|
||||||
|
end
|
||||||
cond.add(Project.allowed_to_condition(user, provider_options[:permission], options)) if provider_options[:permission]
|
cond.add(Project.allowed_to_condition(user, provider_options[:permission], options)) if provider_options[:permission]
|
||||||
|
scope_options[:conditions] = cond.conditions
|
||||||
|
scope_options[:order] = "#{provider_options[:timestamp]} DESC"
|
||||||
|
scope_options[:limit] = options[:limit]
|
||||||
|
|
||||||
with_scope(:find => { :conditions => cond.conditions }) do
|
with_scope(:find => scope_options) do
|
||||||
find(:all, provider_options[:find_options])
|
find(:all, provider_options[:find_options])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue