Merge pull request #192 from warpc/123-activity-feed

[Refs #123] activity feed (commits notifcation not work)
This commit is contained in:
Vladimir Sharshov 2012-02-28 07:46:55 -08:00
commit 529d8d3708
35 changed files with 444 additions and 83 deletions

View File

@ -0,0 +1,7 @@
class ActivityFeedsController < ApplicationController
before_filter :authenticate_user!
def index
@activity_feeds = current_user.activity_feeds.order('created_at DESC')
end
end

View File

@ -256,6 +256,7 @@ class WikiController < ApplicationController
# @committer.after_commit do |committer, sha1|
# here goes callback for notification
# end
ActivityFeedObserver.instance.after_create(@committer).delay
@committer
end

View File

@ -0,0 +1,5 @@
module ActivityFeedsHelper
def render_activity_feed(activity_feed)
render :partial => activity_feed.partial, :locals => activity_feed.data
end
end

View File

@ -0,0 +1,9 @@
class ActivityFeed < ActiveRecord::Base
belongs_to :user
serialize :data
def partial
'activity_feeds/partials/' + self.kind
end
end

View File

@ -0,0 +1,121 @@
class ActivityFeedObserver < ActiveRecord::Observer
observe :issue, :comment, :user
def after_create(record)
case record.class.to_s
when 'User'
ActivityFeed.create(
:user => record,
:kind => 'new_user_notification',
:data => {:user_name => record.name, :user_email => record.email}
)
when 'Issue'
recipients = record.collect_recipient_ids
recipients.each do |recipient_id|
recipient = User.find(recipient_id)
UserMailer.delay.new_issue_notification(record, recipient) if User.find(recipient).notifier.can_notify && User.find(recipient).notifier.new_issue
ActivityFeed.create(
:user => recipient,
:kind => 'new_issue_notification',
:data => {:user_name => recipient.name, :issue_serial_id => record.serial_id, :issue_title => record.title, :project_id => record.project.id, :project_name => record.project.name}
)
end
if record.user_id_was != record.user_id
UserMailer.delay.issue_assign_notification(record, record.user) if record.user.notifier.issue_assign && record.user.notifier.can_notify
ActivityFeed.create(
:user => record.user,
:kind => 'issue_assign_notification',
:data => {:user_name => record.user.name, :issue_serial_id => record.serial_id, :project_id => record.project.id, :issue_title => record.title}
)
end
when 'Comment'
if record.commentable.class == Issue
subscribes = record.commentable.subscribes.finder_hack
subscribes.each do |subscribe|
if record.user_id != subscribe.user_id
UserMailer.delay.new_comment_notification(record, subscribe.user) if record.can_notify_on_new_comment?(subscribe)
ActivityFeed.create(
:user => subscribe.user,
:kind => 'new_comment_notification',
:data => {:user_name => subscribe.user.name, :comment_body => record.body, :issue_title => record.commentable.title,
:issue_serial_id => record.commentable.serial_id, :project_id => record.commentable.project.id}
)
end
end
elsif record.commentable.class == Grit::Commit
subscribes = Subscribe.comment_subscribes(record).where(:status => true)
subscribes.each do |subscribe|
next if record.own_comment?(subscribe.user)
UserMailer.delay.new_comment_notification(record, subscribe.user) if subscribe.user.notifier.can_notify
ActivityFeed.create(
:user => subscribe.user,
:kind => 'new_comment_commit_notification',
:data => {:user_name => subscribe.user.name, :comment_body => record.body, :commit_message => record.commentable.message.encode_to_default,
:commit_id => record.commentable.id, :project_id => record.project.id}
)
end
end
when 'GitHook'
change_type = record.change_type
branch_name = record.refname.match(/\/([\w\d]+)$/)[1]
#user_name = record.
#owner = record.owner
project = Project.find_by_name(record.repo)
last_commits = project.git_repository.repo.log(branch_name, nil).first(3).collect do |commit| #:author => 'author'
[commit.sha, commit.message]
end
if change_type == 'delete'
kind = 'git_delete_branch_notification'
options = {:project_id => project.id, :project_name => project.name, :branch_name => branch_name, :change_type => change_type}
else
kind = 'git_new_push_notification'
options = {:project_id => project.id, :project_name => project.name, :last_commits => last_commits, :branch_name => branch_name, :change_type => change_type}
end
project.owner_and_admin_ids.each do |recipient|
ActivityFeed.create(
:user => User.find(recipient),
:kind => kind,
:data => options
)
end
when 'Gollum::Committer'
actor = User.find_by_uname(record.actor.name)
project_name = record.wiki.path.match(/\/(\w+)\.wiki\.git$/)[1]
project = Project.find_by_name(project_name)
commit_sha = record.commit
#wiki_name = record.wiki.name
project.owner_and_admin_ids.each do |recipient|
ActivityFeed.create(
:user => User.find(recipient),#record.user,
:kind => 'wiki_new_commit_notification',
:data => {:user_id => actor.id, :user_name => actor.name, :project_id => project.id, :project_name => project_name, :commit_sha => commit_sha}
)
end
end
end
def after_update(record)
case record.class.to_s
when 'Issue'
if record.user_id_was != record.user_id
UserMailer.delay.issue_assign_notification(record, record.user) if record.user.notifier.issue_assign && record.user.notifier.can_notify
ActivityFeed.create(
:user => record.user,
:kind => 'issue_assign_notification',
:data => {:user_name => record.user.name, :issue_serial_id => record.serial_id, :project_id => record.project.id, :issue_title => record.title}
)
end
end
end
end

View File

@ -6,9 +6,10 @@ class Comment < ActiveRecord::Base
validates :body, :user_id, :commentable_id, :commentable_type, :presence => true
# FIXME
after_create :subscribe_on_reply, :unless => "commentable_type == 'Grit::Commit'"
after_create :invoke_helper, :if => "commentable_type == 'Grit::Commit'"
after_create :subscribe_users
after_create {|comment| Subscribe.new_comment_notification(comment)}
def helper
class_eval "def commentable; project.git_repository.commit('#{commentable_id}'); end" if commentable_type == 'Grit::Commit'
@ -18,8 +19,20 @@ class Comment < ActiveRecord::Base
user_id == user.id
end
def can_notify_on_reply?(subscribe)
User.find(subscribe.user).notifier.new_comment_reply && User.find(subscribe.user).notifier.can_notify
end
def can_notify_on_new_comment?(subscribe)
User.find(subscribe.user).notifier.new_comment && User.find(subscribe.user).notifier.can_notify
end
protected
def subscribe_on_reply
self.commentable.subscribes.create(:user_id => self.user_id) if !self.commentable.subscribes.exists?(:user_id => self.user_id)
end
def invoke_helper
self.helper
end

View File

@ -16,9 +16,6 @@ class Issue < ActiveRecord::Base
after_create :set_serial_id
after_create :subscribe_users
after_create :deliver_new_issue_notification
after_create :deliver_issue_assign_notification
after_update :deliver_issue_assign_notification
after_update :subscribe_issue_assigned_user
def assign_uname
@ -35,6 +32,14 @@ class Issue < ActiveRecord::Base
end
end
def collect_recipient_ids
recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) }
recipients = recipients | [self.user_id] if self.user_id
recipients = recipients | [self.project.owner_id] if self.project.owner_type == 'User'
recipients
end
protected
def set_serial_id
@ -42,18 +47,6 @@ class Issue < ActiveRecord::Base
self.save!
end
def deliver_new_issue_notification
recipients = collect_recipient_ids
recipients.each do |recipient_id|
recipient = User.find(recipient_id)
UserMailer.delay.new_issue_notification(self, recipient) if User.find(recipient).notifier.can_notify && User.find(recipient).notifier.new_issue
end
end
def deliver_issue_assign_notification
UserMailer.delay.issue_assign_notification(self, self.user) if self.user_id_was != self.user_id && self.user.notifier.issue_assign && self.user.notifier.can_notify
end
def subscribe_users
recipients = collect_recipient_ids
recipients.each do |recipient_id|
@ -62,19 +55,6 @@ class Issue < ActiveRecord::Base
end
end
def collect_recipient_ids
recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) }
recipients = recipients | [self.user_id] if self.user_id
recipients = recipients | [self.project.owner_id] if self.project.owner_type == 'User'
# filter by notification settings
recipients = recipients.select do |recipient|
User.find(recipient).notifier.new_issue && User.find(recipient).notifier.can_notify
end
recipients
end
def subscribe_issue_assigned_user
if self.user_id_was != self.user_id
self.subscribes.where(:user_id => self.user_id_was).first.destroy unless self.user_id_was.blank?

View File

@ -163,7 +163,13 @@ class Project < ActiveRecord::Base
def self.process_hook(owner_uname, repo, newrev, oldrev, ref, newrev_type, oldrev_type)
rec = GitHook.new(owner_uname, repo, newrev, oldrev, ref, newrev_type, oldrev_type)
#ActivityFeedObserver.instance.after_create rec # for example
ActivityFeedObserver.instance.after_create rec
end
def owner_and_admin_ids
recipients = self.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) }
recipients = recipients | [self.owner_id] if self.owner_type == 'User'
recipients
end
protected

View File

@ -14,27 +14,6 @@ class Subscribe < ActiveRecord::Base
Subscribe.where(:subscribeable_id => comment.commentable.id, :subscribeable_type => comment.commentable.class.name, :project_id => comment.project)
end
def self.new_comment_notification(comment)
commentable_class = comment.commentable.class
Subscribe.new_comment_issue_notification(comment) if commentable_class == Issue
Subscribe.new_comment_commit_notification(comment) if commentable_class == Grit::Commit
end
def self.new_comment_issue_notification(comment)
comment.commentable.subscribes.finder_hack.each do |subscribe|
next if comment.own_comment?(subscribe.user) || !subscribe.user.notifier.can_notify
UserMailer.delay.new_comment_notification(comment, subscribe.user) if subscribe.user.notifier.new_comment_reply
end
end
def self.new_comment_commit_notification(comment)
subscribes = Subscribe.comment_subscribes(comment).where(:status => true)
subscribes.each do |subscribe|
next if comment.own_comment?(subscribe.user) || !subscribe.user.notifier.can_notify
UserMailer.delay.new_comment_notification(comment, subscribe.user)
end
end
def self.subscribed_to_commit?(project, user, commit)
subscribe = user.subscribes.where(:subscribeable_id => commit.id, :subscribeable_type => commit.class.name, :project_id => project.id).first
return subscribe.subscribed? if subscribe # return status if already subscribe present
@ -44,12 +23,10 @@ class Subscribe < ActiveRecord::Base
(user.committer?(commit) && user.notifier.new_comment_commit_owner)
end
def self.subscribe_to_commit(options)
Subscribe.set_subscribe_to_commit(options, true)
end
def self.unsubscribe_from_commit(options)
Subscribe.set_subscribe_to_commit(options, false)
end

View File

@ -9,6 +9,8 @@ class User < ActiveRecord::Base
has_one :notifier, :class_name => 'Settings::Notifier' #:notifier
has_many :activity_feeds
has_many :authentications, :dependent => :destroy
has_many :build_lists, :dependent => :destroy
has_many :subscribes, :foreign_key => :user_id, :dependent => :destroy

View File

@ -0,0 +1,21 @@
%a{ :name => "comments" }
.block#block-list
.content
%h2.title
= t("layout.activity_feed.header")
.inner
%ul.list
- @activity_feeds.each do |activity_feed|
%li
.left
= link_to activity_feed.user.uname, user_path(activity_feed.user.uname)
%br
%br
= activity_feed.created_at
%br
%br
.item
= render_activity_feed(activity_feed)
%br
%br
%br

View File

@ -0,0 +1,3 @@
%p== Branch #{ branch_name } has been deleted
%p== Into project #{ link_to(project_name, project_path(project_id)) }

View File

@ -0,0 +1,7 @@
%p== Branch #{ branch_name } has been #{ change_type }d
%p== Into project #{ link_to(project_name, project_path(project_id)) }
- last_commits.each do |commit|
= link_to commit[0], commit_path(project_id, commit[0])
= commit[1]
%br

View File

@ -0,0 +1,3 @@
%p== #{ t("notifications.bodies.issue_assign_notification.title", :user_name => user_name) }
%p= raw t("notifications.bodies.issue_assign_notification.content", :issue_link => link_to(issue_title, project_issue_path(project_id, issue_serial_id)))

View File

@ -0,0 +1,5 @@
%p== #{ t("notifications.bodies.new_comment_notification.title", :user_name => user_name) }
%p= raw t("notifications.bodies.new_comment_notification.content", {:issue_link => link_to(issue_title, project_issue_path(project_id, issue_serial_id))})
%p "#{ comment_body }"

View File

@ -0,0 +1,5 @@
%p== #{ t("notifications.bodies.new_commit_comment_notification.title", :user_name => user_name) }
%p= raw t("notifications.bodies.new_comment_notification.commit_content", {:commit_link => link_to(commit_message, commit_path(project_id, commit_id))})
%p "#{ comment_body }"

View File

@ -0,0 +1,3 @@
%p== #{ t("notifications.bodies.new_issue_notification.title", :user_name => user_name) }
%p= raw t("notifications.bodies.new_issue_notification.content", :issue_link => link_to(issue_title, project_issue_path(project_id, issue_serial_id)), :project_link => link_to(project_name, project_path(project_id)))

View File

@ -0,0 +1,5 @@
%p== #{ t("notifications.bodies.new_user_notification.title", :user_name => user_name) }
%p #{ t("notifications.bodies.new_user_notification.content") }
%p #{ t("notifications.bodies.new_user_notification.email", :user_email => user_email) }

View File

@ -0,0 +1,2 @@
%p== User #{ link_to user_name, user_path(user_id) } has been update wiki #{ link_to "history", compare_versions_project_wiki_index_path(project_id, commit_sha) }
%p== Into project #{ link_to(project_name, project_path(project_id)) }

View File

@ -1,9 +0,0 @@
%p== Hello, #{@user.name}.
- #TODO hmm... this need to be refactored.
%p Your comment into issue #{ link_to @comment.commentable.title, project_issue_url(@comment.commentable.project, @comment.commentable) } has been answered.
%p "#{ @comment.body }"
%p== Support team «ROSA Build System»

View File

@ -6,7 +6,5 @@
%p
==Your email : #{@user.email}
%br/
==Your password: #{@user.password}
%p== Support team «ROSA Build System»

View File

@ -6,7 +6,5 @@
%p
==Ваш email : #{@user.email}
%br/
==Ваш пароль: #{@user.password}
%p== Команда поддержки «ROSA Build System»

View File

@ -25,7 +25,7 @@ module Rosa
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
config.active_record.observers = :event_log_observer
config.active_record.observers = :event_log_observer, :activity_feed_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.

View File

@ -0,0 +1,30 @@
en:
layout:
activity_feed:
header: Activity Feed
notifications:
subjects:
new_comment_notification: New comment to task
new_commit_comment_notification: New comment to commit
new_issue_notification: New task added to project
new_user_notification: Registered on project «%{ project_name }»
issue_assign_notification: New task assigned
bodies:
new_comment_notification:
title: Hello, %{user_name}.
content: To the issue %{issue_link} added a comment.
commit_content: To the commit %{commit_link} added a comment.
new_issue_notification:
title: Hello, %{user_name}.
content: To project %{project_link} has been added an issue %{issue_link}
new_user_notification:
title: Hello, %{user_name}.
content: You have been sign up to project «ROSA Build System» and now can sign in.
email: ==Your email %{user_email}
password: ==Your password %{user_password}
issue_assign_notification:
title: Hello, %{user_name}.
content: You have been assigned to issue %{issue_link}

View File

@ -0,0 +1,31 @@
ru:
layout:
activity_feed:
header: Лента активности
notifications:
subjects:
new_comment_notification: Новый комментарий к задаче
new_commit_comment_notification: Новый комментарий к коммиту
new_issue_notification: Новая задача добавлена к проекту
new_user_notification: Регистрация на проекте «%{ project_name }»
issue_assign_notification: Вам назначили задачу
bodies:
new_comment_notification:
title: Здравствуйте, %{user_name}.
content: К задаче %{issue_link} был добавлен новый комментарий.
commit_content: К коммиту %{commit_link} был добавлен новый комментарий.
new_issue_notification:
title: Здравствуйте, %{user_name}.
content: К проекту %{project_link} была добавлена задача %{issue_link}
new_user_notification:
title: Здравствуйте, %{user_name}.
content: Вы зарегистрированы на проекте «ROSA Build System» и теперь можете войти в систему.
email: ==Ваш email %{user_email}
password: ==Ваш пароль %{user_password}
issue_assign_notification:
title: Здравствуйте, %{user_name}.
content: Вам была назначена задача %{issue_link}
invite_approve_notification: Приглашение в ABF

View File

@ -953,7 +953,6 @@ en:
new_issue_notification: New task added to project
new_user_notification: Registered on project «%{ project_name }»
issue_assign_notification: New task assigned
new_commit_comment_notification: New comment to commit
invite_approve_notification: Invitation to ABF
project:
@ -1036,6 +1035,92 @@ en:
project_version: Version
user: User
build_list/item:
name: Name
level: Level
status: Status
version: Version
build_list: Build list
project:
category_id: Category
name: Name
description: Descripton
owner: Owner
visibility: Visibility
repository_id: Repository
repository: Repository
created_at: Created
updated_at: Updated
has_issues: Tracker on
rpm:
name: Name
arch_id: Arch
arch: Arch
project_id: Project
project: Project
created_at: Created
updated_at: Updated
role:
name: Name
on: Slave
to: Master
use_default: By default
use_default_for_owner: Default by owner
group:
name: Name
uname: Nickname
owner: Owner
created_at: Created
updated_at: Updated
user:
name: User
login: Nickname or Email
email: Email
uname: Nickname
ssh_key: SSH key
current_password: Current password
role: Role
created_at: Created
updated_at: Updated
role: System role
language: Language
product_build_list:
id: Id
product: Product
status: Status
notified_at: Notified at
build_list:
bs_id: Id
name: Name
container_path: Container path
status: Status
project_id: Project
project: Project
arch_id: Architecture
arch: Architecture
is_circle: Recurrent
notified_at: Notified at
additional_repos: Additional repository
include_repos: Includes repository
updated_at: Updated
created_at: Created
pl: Packet list repository
pl_id: Packet list repository
bpl: Platform
bpl_id: Platform
update_type: Update type
build_requires: Dependable build requires
auto_publish: Automated publising
project_version: Version
user: User
build_list/item:
name: Name
level: Level

View File

@ -794,11 +794,3 @@ ru:
platform: Архитектура
counter: Закачки
notifications:
subjects:
new_comment_notification: Новый комментарий к Вашей задаче
new_issue_notification: Новая задача добавлена к проекту
new_user_notification: Регистрация на проекте «%{ project_name }»
issue_assign_notification: Вам назначили задачу
new_commit_comment_notification: Новый комментарий к коммиту
invite_approve_notification: Приглашение в ABF

View File

@ -165,6 +165,8 @@ Rosa::Application.routes.draw do
end
end
resources :activity_feeds, :only => [:index]
resources :users, :groups do
resources :platforms, :only => [:new, :create]
@ -209,6 +211,6 @@ Rosa::Application.routes.draw do
match '/projects/:project_id/git/raw/:treeish/*path', :controller => "git/blobs", :action => :raw, :treeish => /[0-9a-zA-Z_.\-]*/, :defaults => { :treeish => :master }, :as => :raw
match '/projects/:project_id/git/commit/raw/:commit_hash/*path', :controller => "git/blobs", :action => :raw, :as => :raw_commit
root :to => "platforms#index"
root :to => "activity_feeds#index"
match '/forbidden', :to => 'platforms#forbidden', :as => 'forbidden'
end

View File

@ -0,0 +1,15 @@
class CreateActivityFeeds < ActiveRecord::Migration
def self.up
create_table :activity_feeds do |t|
t.integer :user_id, :null => false
t.string :kind
t.text :data
t.timestamps
end
end
def self.down
drop_table :activity_feeds
end
end

View File

@ -13,6 +13,14 @@
ActiveRecord::Schema.define(:version => 20120220185458) do
create_table "activity_feeds", :force => true do |t|
t.integer "user_id", :null => false
t.string "kind"
t.text "data"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "arches", :force => true do |t|
t.string "name", :null => false
t.datetime "created_at"

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe ActivityFeedsController do
end

View File

@ -0,0 +1,6 @@
# Read about factories at http://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :activity_feed do
end
end

View File

@ -0,0 +1,15 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the ActivityFeedsHelper. For example:
#
# describe ActivityFeedsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe ActivityFeedsHelper do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe ActivityFeedObserver do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe ActivityFeed do
pending "add some examples to (or delete) #{__FILE__}"
end