]> source.dussan.org Git - redmine.git/commitdiff
Display latest user's activity on account/show view.
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 30 Nov 2008 11:18:22 +0000 (11:18 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 30 Nov 2008 11:18:22 +0000 (11:18 +0000)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2066 e93f8b46-1217-0410-a6f0-8f06a7374b81

14 files changed:
app/controllers/account_controller.rb
app/helpers/application_helper.rb
app/helpers/projects_helper.rb
app/models/attachment.rb
app/models/changeset.rb
app/models/issue.rb
app/models/journal.rb
app/models/message.rb
app/models/news.rb
app/models/wiki_content.rb
app/views/account/show.rhtml
lib/redmine/activity/fetcher.rb
test/unit/activity_test.rb
vendor/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb

index 4b2ec8317284948495eb9abed6f46ca580a08425..f327dd5b65b6003d8f7c6a7138cd8aa790bf023c 100644 (file)
@@ -1,5 +1,5 @@
-# redMine - project management software
-# Copyright (C) 2006-2007  Jean-Philippe Lang
+# Redmine - project management software
+# Copyright (C) 2006-2008  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -31,6 +31,10 @@ class AccountController < ApplicationController
     @memberships = @user.memberships.select do |membership|
       membership.project.is_public? || (User.current.member_of?(membership.project))
     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
     render_404
   end
index 2487046093f998d9228fc5d2638c00406b499c1b..5995187d709d003a8d42b0e8a90f6ef1fb5b6afc 100644 (file)
@@ -105,6 +105,18 @@ module ApplicationHelper
     @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)
   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)
     from_date = from_date.to_date if from_date.respond_to?(:to_date)
index cd2e743fe86db6a95d2ced664e3f96a4df5059b7..6f5e03349c74db5653e2f4c5d2b7bde15dcbd21f 100644 (file)
@@ -21,18 +21,6 @@ module ProjectsHelper
     link_to h(version.name), { :controller => 'versions', :action => 'show', :id => version }, options
   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
     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},
index 95ba8491ff70fbe08bd570542bacc3100c9feda8..f838076c6e244a523f015d2d4321b0df21bf01af 100644 (file)
@@ -30,12 +30,14 @@ class Attachment < ActiveRecord::Base
 
   acts_as_activity_provider :type => 'files',
                             :permission => :view_files,
+                            :author_key => :author_id,
                             :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 " +
                                                         "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id"}
   
   acts_as_activity_provider :type => 'documents',
                             :permission => :view_documents,
+                            :author_key => :author_id,
                             :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 " +
                                                         "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id"}
index dd38ce757c231e95a20eee5fb8ed82b317114757..759d240df76b4b0a6680ac48adabcd046051435f 100644 (file)
@@ -34,6 +34,7 @@ class Changeset < ActiveRecord::Base
                      :date_column => 'committed_on'
                      
   acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
+                            :author_key => :user_id,
                             :find_options => {:include => {:repository => :project}}
   
   validates_presence_of :repository_id, :revision, :committed_on, :commit_date
index 4701e41f1d6c99e990671258e485899f96a5dbcd..7488850aff119e9448db55aadf51274a63bdf7f1 100644 (file)
@@ -42,7 +42,8 @@ class Issue < ActiveRecord::Base
   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}}                
   
-  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_length_of :subject, :maximum => 255
index 71a51290b1184cef1dd2e348ef30fc6aea430e24..72e7eb91c59079992b7745253a3b4895fffd7e56 100644 (file)
@@ -33,6 +33,7 @@ class Journal < ActiveRecord::Base
 
   acts_as_activity_provider :type => 'issues',
                             :permission => :view_issues,
+                            :author_key => :user_id,
                             :find_options => {:include => [{:issue => :project}, :details, :user],
                                               :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
                                                              " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
index 9a313e822b5923df3ce3c3201188a74c79781d32..acb300f46f699cbe5c13d18d9e4b16112642391c 100644 (file)
@@ -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} : 
                                                                                                                                        {: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
     
   attr_protected :locked, :sticky
index 969b37a0dc0f958b395ab77d136ac2e7f8eed065..5949a731b9087674bcd27f97c673608b7c02f87e 100644 (file)
@@ -26,7 +26,8 @@ class News < ActiveRecord::Base
 
   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_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
   def self.latest(user = User.current, count = 5)
index 4a4c5c270ea3a582bd3e9fa5fdd78777a6a7f774..e958e7b240b262f257db86b26f4253674596a27a 100644 (file)
@@ -37,6 +37,7 @@ class WikiContent < ActiveRecord::Base
 
     acts_as_activity_provider :type => 'wiki_edits',
                               :timestamp => "#{WikiContent.versioned_table_name}.updated_on",
+                              :author_key => "#{WikiContent.versioned_table_name}.author_id",
                               :permission => :view_wiki_edits,
                               :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, " +
index 175a11c752ed57a9c8d922eef5e14cb9991a5e5c..e7b0276f7421804f3418dc301dcb75ad09c52bc3 100644 (file)
@@ -4,6 +4,7 @@
 
 <h2><%= avatar @user %> <%=h @user.name %></h2>
 
+<div class="splitcontentleft">
 <p>
 <%= mail_to(h(@user.mail)) unless @user.pref.hide_mail %>
 <ul>
 <% end %>
 </ul>
 <% end %>
+</div>
+
+<div class="splitcontentright">
 
+<% unless @events_by_day.empty? %>
 <h3><%=l(:label_activity)%></h3>
+
 <p>
 <%=l(:label_reported_issues)%>: <%= Issue.count(:conditions => ["author_id=?", @user.id]) %>
 </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 %>
index adaead56451f61e9a60e4276665ebc9b752afb8a..b12caa441592d4904bfa91ca75bf7fd943169a91 100644 (file)
@@ -25,7 +25,7 @@ module Redmine
       @@constantized_providers = Hash.new {|h,k| h[k] = Redmine::Activity.providers[k].collect {|t| t.constantize } }
       
       def initialize(user, options={})
-        options.assert_valid_keys(:project, :with_subprojects)
+        options.assert_valid_keys(:project, :with_subprojects, :author)
         @user = user
         @project = options[:project]
         @options = options
@@ -58,14 +58,20 @@ module Redmine
       end
       
       # Returns an array of events for the given date range
-      def events(from, to)
+      def events(from = nil, to = nil, options={})
         e = []
+        @options[:limit] = options[:limit]
         
         @scope.each do |event_type|
           constantized_providers(event_type).each do |provider|
             e += provider.find_events(event_type, @user, from, to, @options)
           end
         end
+        
+        if options[:limit]
+          e.sort! {|a,b| b.event_date <=> a.event_date}
+          e = e.slice(0, options[:limit])
+        end
         e
       end
       
index ccda9f119ed4e553d79cfb49b190706cc26ad9b7..e5bc0d2668d62e7f8125b06b1b0f6d9f69ab6a35 100644 (file)
@@ -63,6 +63,15 @@ class ActivityTest < Test::Unit::TestCase
     assert events.include?(Issue.find(4))
   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
   
   def find_events(user, options={})
index 7c4fac8b10b460d800bc81ee8e761d65fd434776..d631935e08fa8649b4ffe17584d45ff4881e08fb 100644 (file)
@@ -29,7 +29,7 @@ module Redmine
             send :include, Redmine::Acts::ActivityProvider::InstanceMethods
           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 ||= {}
           
           # 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[:timestamp] ||= "#{table_name}.created_on"
           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
         end
       end
@@ -54,10 +55,21 @@ module Redmine
             provider_options = activity_provider_options[event_type]
             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]
+            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])
             end
           end