Added generic activity stream support.
This commit is contained in:
parent
9aa5afbd65
commit
4e2288e9a4
21 changed files with 611 additions and 90 deletions
|
@ -40,21 +40,26 @@ class App.DashboardActivityStream extends App.Controller
|
||||||
render: (items) ->
|
render: (items) ->
|
||||||
|
|
||||||
for item in items
|
for item in items
|
||||||
if item.history_object is 'Ticket'
|
if item.object is 'Ticket'
|
||||||
ticket = App.Ticket.find( item.o_id )
|
ticket = App.Ticket.find( item.o_id )
|
||||||
item.link = '#ticket/zoom/' + ticket.id
|
item.link = '#ticket/zoom/' + ticket.id
|
||||||
item.title = ticket.title
|
item.title = ticket.title
|
||||||
item.type = 'Ticket'
|
item.object = 'Ticket'
|
||||||
item.updated_by_id = ticket.updated_by_id
|
|
||||||
item.updated_by = App.User.find( ticket.updated_by_id )
|
else if item.object is 'Ticket::Article'
|
||||||
else if item.history_object is 'Ticket::Article'
|
|
||||||
article = App.TicketArticle.find( item.o_id )
|
article = App.TicketArticle.find( item.o_id )
|
||||||
ticket = App.Ticket.find( article.ticket_id )
|
ticket = App.Ticket.find( article.ticket_id )
|
||||||
item.link = '#ticket/zoom/' + ticket.id + '/' + article.id
|
item.link = '#ticket/zoom/' + ticket.id + '/' + article.id
|
||||||
item.title = article.subject || ticket.title
|
item.title = article.subject || ticket.title
|
||||||
item.type = 'Article'
|
item.object = 'Article'
|
||||||
item.updated_by_id = article.updated_by_id
|
|
||||||
item.updated_by = App.User.find( article.updated_by_id )
|
else if item.object is 'User'
|
||||||
|
user = App.User.find( item.o_id )
|
||||||
|
item.link = '#user/zoom/' + item.o_id
|
||||||
|
item.title = user.displayName()
|
||||||
|
item.object = 'User'
|
||||||
|
|
||||||
|
item.created_by = App.User.find( item.created_by_id )
|
||||||
|
|
||||||
html = App.view('dashboard/activity_stream')(
|
html = App.view('dashboard/activity_stream')(
|
||||||
head: 'Activity Stream',
|
head: 'Activity Stream',
|
||||||
|
@ -67,3 +72,5 @@ class App.DashboardActivityStream extends App.Controller
|
||||||
# start user popups
|
# start user popups
|
||||||
@userPopups('right')
|
@userPopups('right')
|
||||||
|
|
||||||
|
# update time
|
||||||
|
@frontendTimeUpdate()
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
<h2 class="can-move"><%- @T( @head ) %></h2>
|
<h2 class="can-move"><%- @T( @head ) %></h2>
|
||||||
<dl>
|
<dl>
|
||||||
<% for item in @items: %>
|
<% for item in @items: %>
|
||||||
<dt><span class="user-popover" data-id="<%= item.updated_by_id %>">"<%= item.updated_by.displayName() %>"</span></dt>
|
<dt><span class="user-popover" data-id="<%= item.created_by_id %>">"<%= item.created_by.displayName() %>"</span></dt>
|
||||||
<dd><%- @T( item.history_type ) %> <a href="<%- item.link %>"><%= item.type %><% if item.title: %> (<%= item.title %>)<% end %></a>.</dd>
|
<dd><%- @T( item.type ) %> <a href="<%- item.link %>"><%= item.object %><% if item.title: %> (<%= item.title %>)<% end %></a> <span class="humanTimeFromNow" data-time="<%- item.created_at %>">?</span> <%- @T('ago') %>.</dd>
|
||||||
<% end %>
|
<% end %>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class ActivityController < ApplicationController
|
class ActivityStreamController < ApplicationController
|
||||||
before_filter :authentication_check
|
before_filter :authentication_check
|
||||||
|
|
||||||
# GET /api/v1/activity_stream
|
# GET /api/v1/activity_stream
|
||||||
def activity_stream
|
def show
|
||||||
activity_stream = History.activity_stream_fulldata( current_user, params[:limit] )
|
activity_stream = current_user.activity_stream( params[:limit], true )
|
||||||
|
|
||||||
# return result
|
# return result
|
||||||
render :json => activity_stream
|
render :json => activity_stream
|
|
@ -14,6 +14,8 @@ class SessionsController < ApplicationController
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
user.activity_stream_log( 'session started', user.id )
|
||||||
|
|
||||||
# auto population of default collections
|
# auto population of default collections
|
||||||
default_collection = SessionHelper::default_collections(user)
|
default_collection = SessionHelper::default_collections(user)
|
||||||
|
|
||||||
|
|
179
app/models/activity_stream.rb
Normal file
179
app/models/activity_stream.rb
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class ActivityStream < ApplicationModel
|
||||||
|
self.table_name = 'activity_streams'
|
||||||
|
belongs_to :activity_stream_type, :class_name => 'ActivityStream::Type'
|
||||||
|
belongs_to :activity_stream_object, :class_name => 'ActivityStream::Object'
|
||||||
|
|
||||||
|
@@cache_type = {}
|
||||||
|
@@cache_object = {}
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
add a new activity entry for an object
|
||||||
|
|
||||||
|
ActivityStream.add(
|
||||||
|
:type => 'updated',
|
||||||
|
:object => 'Ticket',
|
||||||
|
:role => 'Admin',
|
||||||
|
:o_id => ticket.id,
|
||||||
|
:created_by_id => 1,
|
||||||
|
:created_at => '2013-06-04 10:00:00',
|
||||||
|
)
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.add(data)
|
||||||
|
|
||||||
|
# lookups
|
||||||
|
if data[:type]
|
||||||
|
type = self.type_lookup( data[:type] )
|
||||||
|
end
|
||||||
|
if data[:object]
|
||||||
|
object = self.object_lookup( data[:object] )
|
||||||
|
end
|
||||||
|
|
||||||
|
role_id = nil
|
||||||
|
if data[:role]
|
||||||
|
role_id = Role.lookup( :name => data[:role] )
|
||||||
|
if !role_id
|
||||||
|
raise "No such Role #{data[:role]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# check if entry is needed
|
||||||
|
result = ActivityStream.where(
|
||||||
|
:o_id => data[:o_id],
|
||||||
|
# :activity_stream_type_id => type.id,
|
||||||
|
:role_id => role_id,
|
||||||
|
:activity_stream_object_id => object.id,
|
||||||
|
:created_by_id => data[:created_by_id]
|
||||||
|
).last
|
||||||
|
|
||||||
|
# resturn if old entry is really freash
|
||||||
|
return result if result && result.created_at >= (data[:created_at] - 10.seconds)
|
||||||
|
puts "AS: #{data[:type]} #{data[:object]} #{data[:o_id]}"
|
||||||
|
|
||||||
|
# create history
|
||||||
|
record = {
|
||||||
|
:o_id => data[:o_id],
|
||||||
|
:activity_stream_type_id => type.id,
|
||||||
|
:activity_stream_object_id => object.id,
|
||||||
|
:created_at => data[:created_at],
|
||||||
|
:created_by_id => data[:created_by_id]
|
||||||
|
}
|
||||||
|
ActivityStream.create(record)
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
remove whole activity entries of an object
|
||||||
|
|
||||||
|
ActivityStream.remove( 'Ticket', 123 )
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.remove( object_name, o_id )
|
||||||
|
object = self.object_lookup( object_name )
|
||||||
|
ActivityStream.where(
|
||||||
|
:activity_stream_object_id => object.id,
|
||||||
|
:o_id => o_id,
|
||||||
|
).destroy_all
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
return all activity entries of an user
|
||||||
|
|
||||||
|
activity_stream = ActivityStream.list( user )
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.list(user,limit)
|
||||||
|
# stream = ActivityStream.where( :role_id => user.roles, :group_id => user.groups )
|
||||||
|
stream = ActivityStream.where('1=1').
|
||||||
|
order( 'created_at DESC, id DESC' ).
|
||||||
|
limit( limit )
|
||||||
|
list = []
|
||||||
|
stream.each do |item|
|
||||||
|
data = item.attributes
|
||||||
|
data['object'] = self.object_lookup_id( data['activity_stream_object_id'] ).name
|
||||||
|
data['type'] = self.type_lookup_id( data['activity_stream_type_id'] ).name
|
||||||
|
data.delete('activity_stream_object_id')
|
||||||
|
data.delete('activity_stream_type_id')
|
||||||
|
list.push data
|
||||||
|
end
|
||||||
|
list
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def self.type_lookup_id( id )
|
||||||
|
|
||||||
|
# use cache
|
||||||
|
return @@cache_type[ id ] if @@cache_type[ id ]
|
||||||
|
|
||||||
|
# lookup
|
||||||
|
type = ActivityStream::Type.find(id)
|
||||||
|
@@cache_type[ id ] = type
|
||||||
|
type
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.type_lookup( name )
|
||||||
|
|
||||||
|
# use cache
|
||||||
|
return @@cache_type[ name ] if @@cache_type[ name ]
|
||||||
|
|
||||||
|
# lookup
|
||||||
|
type = ActivityStream::Type.where( :name => name ).first
|
||||||
|
if type
|
||||||
|
@@cache_type[ name ] = type
|
||||||
|
return type
|
||||||
|
end
|
||||||
|
|
||||||
|
# create
|
||||||
|
type = ActivityStream::Type.create(
|
||||||
|
:name => name
|
||||||
|
)
|
||||||
|
@@cache_type[ name ] = type
|
||||||
|
type
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.object_lookup_id( id )
|
||||||
|
|
||||||
|
# use cache
|
||||||
|
return @@cache_object[ id ] if @@cache_object[ id ]
|
||||||
|
|
||||||
|
# lookup
|
||||||
|
object = ActivityStream::Object.find(id)
|
||||||
|
@@cache_object[ id ] = object
|
||||||
|
object
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.object_lookup( name )
|
||||||
|
|
||||||
|
# use cache
|
||||||
|
return @@cache_object[ name ] if @@cache_object[ name ]
|
||||||
|
|
||||||
|
# lookup
|
||||||
|
object = ActivityStream::Object.where( :name => name ).first
|
||||||
|
if object
|
||||||
|
@@cache_object[ name ] = object
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
# create
|
||||||
|
object = ActivityStream::Object.create(
|
||||||
|
:name => name
|
||||||
|
)
|
||||||
|
@@cache_object[ name ] = object
|
||||||
|
object
|
||||||
|
end
|
||||||
|
|
||||||
|
class Object < ApplicationModel
|
||||||
|
end
|
||||||
|
|
||||||
|
class Type < ApplicationModel
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class ApplicationModel < ActiveRecord::Base
|
class ApplicationModel < ActiveRecord::Base
|
||||||
include ApplicationModel::HistoryLogBase
|
include ApplicationModel::HistoryLogBase
|
||||||
|
include ApplicationModel::ActivityStreamBase
|
||||||
|
|
||||||
self.abstract_class = true
|
self.abstract_class = true
|
||||||
|
|
||||||
|
@ -14,6 +15,13 @@ class ApplicationModel < ActiveRecord::Base
|
||||||
after_update :cache_delete
|
after_update :cache_delete
|
||||||
after_destroy :cache_delete
|
after_destroy :cache_delete
|
||||||
|
|
||||||
|
after_create :activity_stream_create
|
||||||
|
after_update :activity_stream_update
|
||||||
|
after_destroy :activity_stream_destroy
|
||||||
|
|
||||||
|
# create instance accessor
|
||||||
|
class << self; attr_accessor :activity_stream_support_config end
|
||||||
|
|
||||||
@@import_class_list = ['Ticket', 'Ticket::Article', 'History', 'Ticket::State', 'Ticket::Priority', 'Group', 'User' ]
|
@@import_class_list = ['Ticket', 'Ticket::Article', 'History', 'Ticket::State', 'Ticket::Priority', 'Group', 'User' ]
|
||||||
|
|
||||||
def check_attributes_protected
|
def check_attributes_protected
|
||||||
|
@ -410,6 +418,59 @@ class OwnModel < ApplicationModel
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
serve methode to configure activity stream support for this model
|
||||||
|
|
||||||
|
class Model < ApplicationModel
|
||||||
|
activity_stream_support :role => 'Admin'
|
||||||
|
end
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.activity_stream_support(data = {})
|
||||||
|
@activity_stream_support_config = data
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
log object create activity stream
|
||||||
|
|
||||||
|
model = Model.find(123)
|
||||||
|
model.activity_stream_create
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def activity_stream_create
|
||||||
|
activity_stream_log( 'created', self['created_by_id'] )
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
log object update activity stream
|
||||||
|
|
||||||
|
model = Model.find(123)
|
||||||
|
model.activity_stream_update
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def activity_stream_update
|
||||||
|
activity_stream_log( 'updated', self['updated_by_id'] )
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
delete object activity stream
|
||||||
|
|
||||||
|
model = Model.find(123)
|
||||||
|
model.activity_stream_destroy
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def activity_stream_destroy
|
||||||
|
ActivityStream.remove( self.class.to_s, self.id )
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
destory object dependencies, will be executed automatically
|
destory object dependencies, will be executed automatically
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
@ -418,6 +479,7 @@ destory object dependencies, will be executed automatically
|
||||||
|
|
||||||
# delete history
|
# delete history
|
||||||
History.remove( self.class.to_s, self.id )
|
History.remove( self.class.to_s, self.id )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
30
app/models/application_model/activity_stream_base.rb
Normal file
30
app/models/application_model/activity_stream_base.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
module ApplicationModel::ActivityStreamBase
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
log activity for this object
|
||||||
|
|
||||||
|
article = Ticket::Article.find(123)
|
||||||
|
result = article.activity_stream_log( 'created', user_id )
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
result = true # false
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def activity_stream_log (type, user_id)
|
||||||
|
return if !self.class.activity_stream_support_config
|
||||||
|
ActivityStream.add(
|
||||||
|
:o_id => self['id'],
|
||||||
|
:type => type,
|
||||||
|
:object => self.class.name,
|
||||||
|
# :role => self.activity_stream_role,
|
||||||
|
:created_at => self.updated_at,
|
||||||
|
:created_by_id => user_id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,8 +1,9 @@
|
||||||
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Group < ApplicationModel
|
class Group < ApplicationModel
|
||||||
has_and_belongs_to_many :users, :after_add => :cache_update, :after_remove => :cache_update
|
has_and_belongs_to_many :users, :after_add => :cache_update, :after_remove => :cache_update
|
||||||
belongs_to :email_address
|
belongs_to :email_address
|
||||||
belongs_to :signature
|
belongs_to :signature
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
end
|
activity_stream_support :role => 'Admin'
|
||||||
|
end
|
|
@ -95,6 +95,7 @@ remove whole history entries of an object
|
||||||
|
|
||||||
def self.remove( requested_object, requested_object_id )
|
def self.remove( requested_object, requested_object_id )
|
||||||
history_object = History::Object.where( :name => requested_object ).first
|
history_object = History::Object.where( :name => requested_object ).first
|
||||||
|
return if !history_object
|
||||||
History.where(
|
History.where(
|
||||||
:history_object_id => history_object.id,
|
:history_object_id => history_object.id,
|
||||||
:o_id => requested_object_id,
|
:o_id => requested_object_id,
|
||||||
|
@ -103,7 +104,7 @@ remove whole history entries of an object
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
return all histoy entries of an object
|
return all history entries of an object
|
||||||
|
|
||||||
history_list = History.list( 'Ticket', 123 )
|
history_list = History.list( 'Ticket', 123 )
|
||||||
|
|
||||||
|
@ -133,56 +134,6 @@ return all histoy entries of an object
|
||||||
return history
|
return history
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.activity_stream( user, limit = 10 )
|
|
||||||
# g = Group.where( :active => true ).joins(:users).where( 'users.id' => user.id )
|
|
||||||
# stream = History.select("distinct(histories.o_id), created_by_id, history_attribute_id, history_type_id, history_object_id, value_from, value_to").
|
|
||||||
# where( :history_type_id => History::Type.where( :name => ['created', 'updated']) ).
|
|
||||||
stream = History.select("distinct(histories.o_id), created_by_id, history_type_id, history_object_id").
|
|
||||||
where( :history_object_id => History::Object.where( :name => [ 'Ticket', 'Ticket::Article' ] ) ).
|
|
||||||
where( :history_type_id => History::Type.where( :name => [ 'created', 'updated' ]) ).
|
|
||||||
order('created_at DESC, id DESC').
|
|
||||||
limit(limit)
|
|
||||||
datas = []
|
|
||||||
stream.each do |item|
|
|
||||||
data = item.attributes
|
|
||||||
data['history_object'] = self.object_lookup_id( data['history_object_id'] ).name
|
|
||||||
data['history_type'] = self.type_lookup_id( data['history_type_id'] ).name
|
|
||||||
data.delete('history_object_id')
|
|
||||||
data.delete('history_type_id')
|
|
||||||
datas.push data
|
|
||||||
# item['history_attribute'] = item.history_attribute
|
|
||||||
end
|
|
||||||
return datas
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.activity_stream_fulldata( user, limit = 10 )
|
|
||||||
activity_stream = History.activity_stream( user, limit )
|
|
||||||
|
|
||||||
# get related users
|
|
||||||
assets = {}
|
|
||||||
activity_stream.each {|item|
|
|
||||||
|
|
||||||
# load article ids
|
|
||||||
if item['history_object'] == 'Ticket'
|
|
||||||
ticket = Ticket.find( item['o_id'] )
|
|
||||||
assets = ticket.assets(assets)
|
|
||||||
end
|
|
||||||
if item['history_object'] == 'Ticket::Article'
|
|
||||||
article = Ticket::Article.find( item['o_id'] )
|
|
||||||
assets = article.assets(assets)
|
|
||||||
end
|
|
||||||
if item['history_object'] == 'User'
|
|
||||||
user = User.find( item['o_id'] )
|
|
||||||
assets = user.assets(assets)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
:activity_stream => activity_stream,
|
|
||||||
:assets => assets,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.type_lookup_id( id )
|
def self.type_lookup_id( id )
|
||||||
|
|
|
@ -20,10 +20,6 @@ class Observer::History < ActiveRecord::Observer
|
||||||
record.history_create( 'created', user_id )
|
record.history_create( 'created', user_id )
|
||||||
end
|
end
|
||||||
|
|
||||||
# log activity stream
|
|
||||||
if record.respond_to?('activity_stream')
|
|
||||||
record.activity_stream( 'created', user_id )
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def before_update(record)
|
def before_update(record)
|
||||||
|
@ -149,12 +145,6 @@ class Observer::History < ActiveRecord::Observer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# log activity stream
|
|
||||||
if history_logged
|
|
||||||
if record.respond_to?('activity_stream')
|
|
||||||
record.activity_stream( 'updated', user_id )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def differences_from?(one, other)
|
def differences_from?(one, other)
|
||||||
|
|
|
@ -4,7 +4,8 @@ class Organization < ApplicationModel
|
||||||
include Organization::Assets
|
include Organization::Assets
|
||||||
extend Organization::Search
|
extend Organization::Search
|
||||||
|
|
||||||
has_and_belongs_to_many :users
|
has_and_belongs_to_many :users
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
|
activity_stream_support :role => 'Admin'
|
||||||
|
|
||||||
end
|
end
|
|
@ -1,6 +1,7 @@
|
||||||
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Role < ApplicationModel
|
class Role < ApplicationModel
|
||||||
has_and_belongs_to_many :users, :after_add => :cache_update, :after_remove => :cache_update
|
has_and_belongs_to_many :users, :after_add => :cache_update, :after_remove => :cache_update
|
||||||
validates :name, :presence => true
|
validates :name, :presence => true
|
||||||
|
activity_stream_support :role => 'Admin'
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Ticket < ApplicationModel
|
||||||
after_create :notify_clients_after_create
|
after_create :notify_clients_after_create
|
||||||
after_update :notify_clients_after_update
|
after_update :notify_clients_after_update
|
||||||
after_destroy :notify_clients_after_destroy
|
after_destroy :notify_clients_after_destroy
|
||||||
|
activity_stream_support :role => 'User'
|
||||||
|
|
||||||
belongs_to :group
|
belongs_to :group
|
||||||
has_many :articles, :class_name => 'Ticket::Article', :after_add => :cache_update, :after_remove => :cache_update
|
has_many :articles, :class_name => 'Ticket::Article', :after_add => :cache_update, :after_remove => :cache_update
|
||||||
|
|
|
@ -4,15 +4,17 @@ class Ticket::Article < ApplicationModel
|
||||||
include Ticket::Article::Assets
|
include Ticket::Article::Assets
|
||||||
include Ticket::Article::HistoryLog
|
include Ticket::Article::HistoryLog
|
||||||
|
|
||||||
after_create :attachment_check
|
|
||||||
belongs_to :ticket
|
belongs_to :ticket
|
||||||
belongs_to :ticket_article_type, :class_name => 'Ticket::Article::Type'
|
belongs_to :ticket_article_type, :class_name => 'Ticket::Article::Type'
|
||||||
belongs_to :ticket_article_sender, :class_name => 'Ticket::Article::Sender'
|
belongs_to :ticket_article_sender, :class_name => 'Ticket::Article::Sender'
|
||||||
belongs_to :created_by, :class_name => 'User'
|
belongs_to :created_by, :class_name => 'User'
|
||||||
|
after_create :attachment_check
|
||||||
after_create :notify_clients_after_create
|
after_create :notify_clients_after_create
|
||||||
after_update :notify_clients_after_update
|
after_update :notify_clients_after_update
|
||||||
after_destroy :notify_clients_after_destroy
|
after_destroy :notify_clients_after_destroy
|
||||||
|
|
||||||
|
activity_stream_support
|
||||||
|
|
||||||
attr_accessor :attachments
|
attr_accessor :attachments
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -19,6 +19,8 @@ class User < ApplicationModel
|
||||||
|
|
||||||
store :preferences
|
store :preferences
|
||||||
|
|
||||||
|
activity_stream_support :role => 'Admin'
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
fullname of user
|
fullname of user
|
||||||
|
@ -43,7 +45,7 @@ returns
|
||||||
end
|
end
|
||||||
fullname = fullname + self.lastname
|
fullname = fullname + self.lastname
|
||||||
end
|
end
|
||||||
return fullname
|
fullname
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
@ -68,6 +70,52 @@ returns
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
get users activity stream
|
||||||
|
|
||||||
|
user = User.find(123)
|
||||||
|
result = user.activity_stream( 20 )
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
result = [
|
||||||
|
{
|
||||||
|
:id =>2,
|
||||||
|
:o_id =>2,
|
||||||
|
:created_by_id => 3,
|
||||||
|
:created_at => '2013-09-28 00:57:21',
|
||||||
|
:object => "User",
|
||||||
|
:type => "created",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:id =>2,
|
||||||
|
:o_id =>2,
|
||||||
|
:created_by_id => 3,
|
||||||
|
:created_at => '2013-09-28 00:59:21',
|
||||||
|
:object => "User",
|
||||||
|
:type => "updated",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def activity_stream( limit, fulldata = false )
|
||||||
|
activity_stream = ActivityStream.list( self, limit )
|
||||||
|
return activity_stream if !fulldata
|
||||||
|
|
||||||
|
# get related objects
|
||||||
|
assets = {}
|
||||||
|
activity_stream.each {|item|
|
||||||
|
record = Kernel.const_get( item['object'] ).find( item['o_id'] )
|
||||||
|
assets = record.assets(assets)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
:activity_stream => activity_stream,
|
||||||
|
:assets => assets,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
authenticate user
|
authenticate user
|
||||||
|
|
||||||
result = User.authenticate(username, password)
|
result = User.authenticate(username, password)
|
||||||
|
|
|
@ -28,6 +28,12 @@ returns
|
||||||
if !data[ User.to_app_model ][ self.id ]
|
if !data[ User.to_app_model ][ self.id ]
|
||||||
data[ User.to_app_model ][ self.id ] = User.user_data_full( self.id )
|
data[ User.to_app_model ][ self.id ] = User.user_data_full( self.id )
|
||||||
end
|
end
|
||||||
|
if !data[ User.to_app_model ][ self['created_by_id'] ]
|
||||||
|
data[ User.to_app_model ][ self['created_by_id'] ] = User.user_data_full( self['created_by_id'] )
|
||||||
|
end
|
||||||
|
if !data[ User.to_app_model ][ self['updated_by_id'] ]
|
||||||
|
data[ User.to_app_model ][ self['updated_by_id'] ] = User.user_data_full( self['updated_by_id'] )
|
||||||
|
end
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ module Zammad
|
||||||
# -- all .rb files in that directory are automatically loaded.
|
# -- all .rb files in that directory are automatically loaded.
|
||||||
|
|
||||||
# Custom directories with classes and modules you want to be autoloadable.
|
# Custom directories with classes and modules you want to be autoloadable.
|
||||||
config.autoload_paths += Dir["#{config.root}/lib/*", "#{config.root}/lib/**/"]
|
# config.autoload_paths += Dir["#{config.root}/lib/*", "#{config.root}/lib/**/"]
|
||||||
# config.autoload_paths += %W(#{config.root}/lib)
|
config.autoload_paths += %W(#{config.root}/lib)
|
||||||
|
|
||||||
# Only load the plugins named here, in the order given (default is alphabetical).
|
# Only load the plugins named here, in the order given (default is alphabetical).
|
||||||
# :all can be used as a placeholder for all plugins not explicitly named.
|
# :all can be used as a placeholder for all plugins not explicitly named.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Zammad::Application.routes.draw do
|
Zammad::Application.routes.draw do
|
||||||
api_path = Rails.configuration.api_path
|
api_path = Rails.configuration.api_path
|
||||||
|
|
||||||
match api_path + '/activity_stream', :to => 'activity#activity_stream', :via => :get
|
match api_path + '/activity_stream', :to => 'activity_stream#show', :via => :get
|
||||||
|
|
||||||
end
|
end
|
41
db/migrate/20130925000001_create_activity_stream.rb
Normal file
41
db/migrate/20130925000001_create_activity_stream.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
class CreateActivityStream < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
|
||||||
|
create_table :activity_streams do |t|
|
||||||
|
t.references :activity_stream_type, :null => false
|
||||||
|
t.references :activity_stream_object, :null => false
|
||||||
|
t.references :role, :null => true
|
||||||
|
t.references :group, :null => true
|
||||||
|
t.column :o_id, :integer, :null => false
|
||||||
|
t.column :created_by_id, :integer, :null => false
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
add_index :activity_streams, [:o_id]
|
||||||
|
add_index :activity_streams, [:created_by_id]
|
||||||
|
add_index :activity_streams, [:role_id]
|
||||||
|
add_index :activity_streams, [:group_id]
|
||||||
|
add_index :activity_streams, [:created_at]
|
||||||
|
add_index :activity_streams, [:activity_stream_object_id]
|
||||||
|
add_index :activity_streams, [:activity_stream_type_id]
|
||||||
|
|
||||||
|
create_table :activity_stream_types do |t|
|
||||||
|
t.column :name, :string, :limit => 250, :null => false
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
add_index :activity_stream_types, [:name], :unique => true
|
||||||
|
|
||||||
|
create_table :activity_stream_objects do |t|
|
||||||
|
t.column :name, :string, :limit => 250, :null => false
|
||||||
|
t.column :note, :string, :limit => 250, :null => true
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
add_index :activity_stream_objects, [:name], :unique => true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_table :activity_streams
|
||||||
|
drop_table :activity_stream_objects
|
||||||
|
drop_table :activity_stream_types
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,13 +4,13 @@ module Sessions::Backend::ActivityStream
|
||||||
def self.worker( user, worker )
|
def self.worker( user, worker )
|
||||||
cache_key = 'user_' + user.id.to_s + '_activity_stream'
|
cache_key = 'user_' + user.id.to_s + '_activity_stream'
|
||||||
if Sessions::CacheIn.expired(cache_key)
|
if Sessions::CacheIn.expired(cache_key)
|
||||||
activity_stream = History.activity_stream( user, 20 )
|
activity_stream = user.activity_stream( 20 )
|
||||||
activity_stream_cache = Sessions::CacheIn.get( cache_key, { :re_expire => true } )
|
activity_stream_cache = Sessions::CacheIn.get( cache_key, { :re_expire => true } )
|
||||||
worker.log 'notice', 'fetch activity_stream - ' + cache_key
|
worker.log 'notice', 'fetch activity_stream - ' + cache_key
|
||||||
if activity_stream != activity_stream_cache
|
if activity_stream != activity_stream_cache
|
||||||
worker.log 'notify', 'fetch activity_stream changed - ' + cache_key
|
worker.log 'notify', 'fetch activity_stream changed - ' + cache_key
|
||||||
|
|
||||||
activity_stream_full = History.activity_stream_fulldata( user, 20 )
|
activity_stream_full = user.activity_stream( 20, true )
|
||||||
Sessions::CacheIn.set( cache_key, activity_stream, { :expires_in => 0.75.minutes } )
|
Sessions::CacheIn.set( cache_key, activity_stream, { :expires_in => 0.75.minutes } )
|
||||||
Sessions::CacheIn.set( cache_key + '_push', activity_stream_full )
|
Sessions::CacheIn.set( cache_key + '_push', activity_stream_full )
|
||||||
end
|
end
|
||||||
|
|
199
test/unit/activity_stream_test.rb
Normal file
199
test/unit/activity_stream_test.rb
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class ActivityStreamTest < ActiveSupport::TestCase
|
||||||
|
test 'ticket+user' do
|
||||||
|
tests = [
|
||||||
|
|
||||||
|
# test 1
|
||||||
|
{
|
||||||
|
:create => {
|
||||||
|
:ticket => {
|
||||||
|
:group_id => Group.lookup( :name => 'Users' ).id,
|
||||||
|
:customer_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
|
||||||
|
:owner_id => User.lookup( :login => '-' ).id,
|
||||||
|
:title => 'Unit Test 1 (äöüß)!',
|
||||||
|
:ticket_state_id => Ticket::State.lookup( :name => 'new' ).id,
|
||||||
|
:ticket_priority_id => Ticket::Priority.lookup( :name => '2 normal' ).id,
|
||||||
|
:updated_by_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
|
||||||
|
:created_by_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
|
||||||
|
},
|
||||||
|
:article => {
|
||||||
|
:updated_by_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
|
||||||
|
:created_by_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
|
||||||
|
:ticket_article_type_id => Ticket::Article::Type.lookup( :name => 'phone' ).id,
|
||||||
|
:ticket_article_sender_id => Ticket::Article::Sender.lookup( :name => 'Customer' ).id,
|
||||||
|
:from => 'Unit Test <unittest@example.com>',
|
||||||
|
:body => 'Unit Test 123',
|
||||||
|
:internal => false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
:update => {
|
||||||
|
:ticket => {
|
||||||
|
:title => 'Unit Test 1 (äöüß) - update!',
|
||||||
|
:ticket_state_id => Ticket::State.lookup( :name => 'open' ).id,
|
||||||
|
:ticket_priority_id => Ticket::Priority.lookup( :name => '1 low' ).id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
:check => [
|
||||||
|
{
|
||||||
|
:object => 'Ticket',
|
||||||
|
:type => 'created',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:object => 'Ticket::Article',
|
||||||
|
:type => 'created',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:object => 'User',
|
||||||
|
:type => 'updated',
|
||||||
|
:o_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
tickets = []
|
||||||
|
tests.each { |test|
|
||||||
|
|
||||||
|
ticket = nil
|
||||||
|
article = nil
|
||||||
|
|
||||||
|
# use transaction
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
ticket = Ticket.create( test[:create][:ticket] )
|
||||||
|
test[:check][0][:o_id] = ticket.id
|
||||||
|
test[:check][0][:created_at] = ticket.created_at
|
||||||
|
test[:check][0][:created_by_id] = User.lookup( :login => 'nicole.braun@zammad.org' ).id
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
test[:create][:article][:ticket_id] = ticket.id
|
||||||
|
article = Ticket::Article.create( test[:create][:article] )
|
||||||
|
test[:check][1][:o_id] = article.id
|
||||||
|
test[:check][1][:created_at] = article.created_at
|
||||||
|
test[:check][1][:created_by_id] = User.lookup( :login => 'nicole.braun@zammad.org' ).id
|
||||||
|
|
||||||
|
assert_equal( ticket.class.to_s, 'Ticket' )
|
||||||
|
assert_equal( article.class.to_s, 'Ticket::Article' )
|
||||||
|
|
||||||
|
# update ticket
|
||||||
|
if test[:update][:ticket]
|
||||||
|
ticket.update_attributes( test[:update][:ticket] )
|
||||||
|
test[:check][2][:o_id] = ticket.id
|
||||||
|
test[:check][2][:created_at] = ticket.created_at
|
||||||
|
test[:check][2][:created_by_id] = User.lookup( :login => 'nicole.braun@zammad.org' ).id
|
||||||
|
end
|
||||||
|
if test[:update][:article]
|
||||||
|
article.update_attributes( test[:update][:article] )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# remember ticket
|
||||||
|
tickets.push ticket
|
||||||
|
|
||||||
|
# check activity_stream
|
||||||
|
activity_stream_check( User.find(1).activity_stream(3), test[:check] )
|
||||||
|
}
|
||||||
|
|
||||||
|
# delete tickets
|
||||||
|
tickets.each { |ticket|
|
||||||
|
ticket_id = ticket.id
|
||||||
|
ticket.destroy
|
||||||
|
found = Ticket.where( :id => ticket_id ).first
|
||||||
|
assert( !found, "Ticket destroyed")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'organization' do
|
||||||
|
tests = [
|
||||||
|
|
||||||
|
# test 1
|
||||||
|
{
|
||||||
|
:create => {
|
||||||
|
:organization => {
|
||||||
|
:name => 'some name',
|
||||||
|
:updated_by_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
|
||||||
|
:created_by_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
:update1 => {
|
||||||
|
:organization => {
|
||||||
|
:name => 'some name (äöüß)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
:update2 => {
|
||||||
|
:organization => {
|
||||||
|
:name => 'some name 2 (äöüß)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
:check => [
|
||||||
|
{
|
||||||
|
:object => 'Organization',
|
||||||
|
:type => 'created',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:object => 'Organization',
|
||||||
|
:type => 'updated',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
organizations = []
|
||||||
|
tests.each { |test|
|
||||||
|
|
||||||
|
organization = Organization.create( test[:create][:organization] )
|
||||||
|
test[:check][0][:o_id] = organization.id
|
||||||
|
test[:check][0][:created_at] = organization.created_at
|
||||||
|
test[:check][0][:created_by_id] = User.lookup( :login => 'nicole.braun@zammad.org' ).id
|
||||||
|
sleep 11
|
||||||
|
|
||||||
|
assert_equal( organization.class.to_s, 'Organization' )
|
||||||
|
|
||||||
|
if test[:update1][:organization]
|
||||||
|
organization.update_attributes( test[:update1][:organization] )
|
||||||
|
test[:check][1][:o_id] = organization.id
|
||||||
|
test[:check][1][:updated_at] = organization.updated_at
|
||||||
|
test[:check][1][:created_by_id] = User.lookup( :login => 'nicole.braun@zammad.org' ).id
|
||||||
|
sleep 2
|
||||||
|
end
|
||||||
|
|
||||||
|
if test[:update2][:organization]
|
||||||
|
organization.update_attributes( test[:update2][:organization] )
|
||||||
|
end
|
||||||
|
|
||||||
|
# remember organization
|
||||||
|
organizations.push organization
|
||||||
|
|
||||||
|
# check activity_stream
|
||||||
|
activity_stream_check( User.find(1).activity_stream(2), test[:check] )
|
||||||
|
}
|
||||||
|
|
||||||
|
# delete tickets
|
||||||
|
organizations.each { |organization|
|
||||||
|
organization_id = organization.id
|
||||||
|
organization.destroy
|
||||||
|
found = Organization.where( :id => organization_id ).first
|
||||||
|
assert( !found, "Organization destroyed")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def activity_stream_check( activity_stream_list, checks )
|
||||||
|
checks.each { |check_item|
|
||||||
|
# puts '+++++++++++'
|
||||||
|
# puts check_item.inspect
|
||||||
|
match = false
|
||||||
|
activity_stream_list.each { |item|
|
||||||
|
next if match
|
||||||
|
# puts '--------'
|
||||||
|
# puts item.inspect
|
||||||
|
# puts item.object
|
||||||
|
next if item['object'] != check_item[:object]
|
||||||
|
next if item['type'] != check_item[:type]
|
||||||
|
next if item['o_id'] != check_item[:o_id]
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
assert( match, "activity stream check not matched! #{check_item.inspect}")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue