Implemented server push if ticket, article or user has changed.
This commit is contained in:
parent
8fd45e7297
commit
ff5d44bd24
31 changed files with 111 additions and 50 deletions
|
@ -1,8 +1,15 @@
|
|||
class App.Controller extends Spine.Controller
|
||||
@include App.Log
|
||||
|
||||
constructor: ->
|
||||
constructor: (params) ->
|
||||
|
||||
# unbind old bindlings
|
||||
if params && params.el && params.el.unbind
|
||||
params.el.unbind()
|
||||
|
||||
super
|
||||
|
||||
# create shortcuts
|
||||
@Config = App.Config
|
||||
@Session = App.Session
|
||||
|
||||
|
|
|
@ -20,7 +20,21 @@ class App.TicketZoom extends App.Controller
|
|||
@load(cache)
|
||||
update = =>
|
||||
@fetch( @ticket_id, false )
|
||||
@interval( update, 30000, @key, 'ticket_zoom' )
|
||||
@interval( update, 120000, @key, 'ticket_zoom' )
|
||||
|
||||
# fetch new data if triggered
|
||||
App.Event.bind(
|
||||
'ticket:updated'
|
||||
(data) =>
|
||||
update = =>
|
||||
if data.id.toString() is @ticket_id.toString()
|
||||
ticket = App.Collection.find( 'Ticket', @ticket_id )
|
||||
console.log('TRY', data.updated_at, ticket.updated_at)
|
||||
if data.updated_at isnt ticket.updated_at
|
||||
@fetch( @ticket_id, false )
|
||||
@delay( update, 2000, 'ticket-zoom-' + @ticket_id )
|
||||
'ticket-zoom-' + @ticket_id
|
||||
)
|
||||
|
||||
meta: =>
|
||||
return if !@ticket
|
||||
|
@ -43,6 +57,7 @@ class App.TicketZoom extends App.Controller
|
|||
return true
|
||||
|
||||
release: =>
|
||||
App.Event.unbindLevel 'ticket-zoom-' + @ticket_id
|
||||
@clearInterval( @key, 'ticket_zoom' )
|
||||
@el.remove()
|
||||
|
||||
|
@ -75,13 +90,12 @@ class App.TicketZoom extends App.Controller
|
|||
# return if ticket hasnt changed
|
||||
return if _.isEqual( @dataLastCall.ticket, data.ticket )
|
||||
|
||||
# return if ticket changed by my self
|
||||
return if data.ticket.updated_by_id is @Session.all().id
|
||||
|
||||
# trigger task notify
|
||||
diff = difference( @dataLastCall.ticket, data.ticket )
|
||||
console.log('diff', diff)
|
||||
if !_.isEmpty(diff)
|
||||
|
||||
# notify if ticket changed not by my self
|
||||
if !_.isEmpty(diff) && data.ticket.updated_by_id isnt @Session.all().id
|
||||
App.TaskManager.notify( @task_key )
|
||||
|
||||
# remember current data
|
||||
|
@ -601,7 +615,7 @@ class ArticleView extends App.Controller
|
|||
#@ui.el.find('[name="cc"]').val('')
|
||||
#@ui.el.find('[name="subject"]').val('')
|
||||
@ui.el.find('[name="in_reply_to"]').val('')
|
||||
console.log('repl2', article_type.name)
|
||||
|
||||
if article.message_id
|
||||
@ui.el.find('[name="in_reply_to"]').val(article.message_id)
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
class App.Model extends Spine.Model
|
||||
@destroyBind: false
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
# delete object from local storage on destroy
|
||||
@bind( 'destroy', (e) ->
|
||||
className = Object.getPrototypeOf(e).constructor.className
|
||||
key = "collection::#{className}::#{e.id}"
|
||||
App.Store.delete(key)
|
||||
)
|
||||
if !@constructor.destroyBind
|
||||
@bind( 'destroy', (e) ->
|
||||
className = Object.getPrototypeOf(e).constructor.className
|
||||
key = "collection::#{className}::#{e.id}"
|
||||
App.Store.delete(key)
|
||||
)
|
||||
|
||||
displayName: ->
|
||||
return @name if @name
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class App.Channel extends App.Model
|
||||
@configure 'Channel', 'adapter', 'area', 'options', 'group_id', 'active'
|
||||
@configure 'Channel', 'adapter', 'area', 'options', 'group_id', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/channels'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.EmailAddress extends App.Model
|
||||
@configure 'EmailAddress', 'realname', 'email', 'note', 'active'
|
||||
@configure 'EmailAddress', 'realname', 'email', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/email_addresses'
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Group extends App.Model
|
||||
@configure 'Group', 'name', 'assignment_timeout', 'follow_up_possible', 'follow_up_assignment', 'email_address_id', 'signature_id', 'note', 'active'
|
||||
@configure 'Group', 'name', 'assignment_timeout', 'follow_up_possible', 'follow_up_assignment', 'email_address_id', 'signature_id', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/groups'
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Network extends App.Model
|
||||
@configure 'Network', 'name', 'note', 'active'
|
||||
@configure 'Network', 'name', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@configure_attributes = [
|
||||
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, 'null': false, 'class': 'xlarge' },
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
class App.NetworkCategory extends App.Model
|
||||
@configure 'NetworkCategory', 'name', 'network_id', 'network_category_type_id', 'network_privacy_id', 'note', 'allow_comments', 'active'
|
||||
@configure 'NetworkCategory', 'name', 'network_id', 'network_category_type_id', 'network_privacy_id', 'note', 'allow_comments', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
class App.NetworkCategoryType extends App.Model
|
||||
@configure 'NetworkCategoryType', 'name', 'note', 'active'
|
||||
@configure 'NetworkCategoryType', 'name', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
class App.NetworkPrivacy extends App.Model
|
||||
@configure 'NetworkPrivacy', 'name', 'key'
|
||||
@configure 'NetworkPrivacy', 'name', 'key', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Organization extends App.Model
|
||||
@configure 'Organization', 'name', 'shared', 'note', 'active'
|
||||
@configure 'Organization', 'name', 'shared', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/organizations'
|
||||
@configure_attributes = [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Overview extends Spine.Model
|
||||
@configure 'Overview', 'name', 'link', 'prio', 'condition', 'order', 'group_by', 'view', 'user_id', 'organization_shared', 'role_id', 'order', 'group_by', 'active'
|
||||
@configure 'Overview', 'name', 'link', 'prio', 'condition', 'order', 'group_by', 'view', 'user_id', 'organization_shared', 'role_id', 'order', 'group_by', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/overviews'
|
||||
@configure_attributes = [
|
||||
|
@ -97,4 +97,4 @@ class App.Overview extends Spine.Model
|
|||
'link',
|
||||
'role',
|
||||
'prio',
|
||||
]
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.PostmasterFilter extends App.Model
|
||||
@configure 'PostmasterFilter', 'name', 'channel', 'match', 'perform', 'note', 'active'
|
||||
@configure 'PostmasterFilter', 'name', 'channel', 'match', 'perform', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/postmaster_filters'
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Role extends App.Model
|
||||
@configure 'Role', 'name', 'note', 'active'
|
||||
@configure 'Role', 'name', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/roles'
|
||||
@configure_attributes = [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Signature extends App.Model
|
||||
@configure 'Signature', 'name', 'body', 'note', 'active'
|
||||
@configure 'Signature', 'name', 'body', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/signatures'
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Sla extends App.Model
|
||||
@configure 'Sla', 'name', 'first_response_time', 'update_time', 'close_time', 'condition', 'timezone', 'data', 'active'
|
||||
@configure 'Sla', 'name', 'first_response_time', 'update_time', 'close_time', 'condition', 'timezone', 'data', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/slas'
|
||||
@configure_attributes = [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Taskbar extends App.Model
|
||||
@configure 'Taskbar', 'key', 'client_id', 'callback', 'state', 'params', 'prio', 'notify', 'active'
|
||||
@configure 'Taskbar', 'key', 'client_id', 'callback', 'state', 'params', 'prio', 'notify', 'active', 'updated_at'
|
||||
# @extend Spine.Model.Local
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/taskbar'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class App.Template extends App.Model
|
||||
@configure 'Template', 'name', 'options', 'group_ids', 'user_id'
|
||||
@configure 'Template', 'name', 'options', 'group_ids', 'user_id', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/templates'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.TextModule extends App.Model
|
||||
@configure 'TextModule', 'name', 'keywords', 'content', 'active', 'group_ids', 'user_id'
|
||||
@configure 'TextModule', 'name', 'keywords', 'content', 'active', 'group_ids', 'user_id', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/text_modules'
|
||||
@configure_attributes = [
|
||||
|
@ -14,4 +14,4 @@ class App.TextModule extends App.Model
|
|||
'name',
|
||||
'keywords',
|
||||
'content',
|
||||
]
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.Ticket extends App.Model
|
||||
@configure 'Ticket', 'number', 'title', 'group_id', 'owner_id', 'customer_id', 'ticket_state_id', 'ticket_priority_id', 'article', 'tags'
|
||||
@configure 'Ticket', 'number', 'title', 'group_id', 'owner_id', 'customer_id', 'ticket_state_id', 'ticket_priority_id', 'article', 'tags', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/tickets'
|
||||
@configure_attributes = [
|
||||
|
@ -18,4 +18,4 @@ class App.Ticket extends App.Model
|
|||
{ name: 'close_time', display: 'Close time', tag: 'time', null: true, style: 'width: 12%' },
|
||||
{ name: 'escalation_time', display: 'Escalation in', tag: 'time', null: true, style: 'width: 12%' },
|
||||
{ name: 'article_count', display: 'Article#', style: 'width: 12%' },
|
||||
]
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.TicketArticle extends App.Model
|
||||
@configure 'TicketArticle', 'from', 'to', 'cc', 'subject', 'body', 'ticket_id', 'ticket_article_type_id', 'ticket_article_sender_id', 'internal', 'in_reply_to', 'form_id'
|
||||
@configure 'TicketArticle', 'from', 'to', 'cc', 'subject', 'body', 'ticket_id', 'ticket_article_type_id', 'ticket_article_sender_id', 'internal', 'in_reply_to', 'form_id', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/ticket_articles'
|
||||
@configure_attributes = [
|
||||
|
@ -12,4 +12,4 @@ class App.TicketArticle extends App.Model
|
|||
{ name: 'ticket_article_type_id', display: 'Type', tag: 'select', multiple: false, null: false, relation: 'TicketArticleType', default: '', class: 'medium' },
|
||||
{ name: 'ticket_article_sender_id', display: 'Sender', tag: 'select', multiple: false, null: false, relation: 'TicketArticleSender', default: '', class: 'medium' },
|
||||
{ name: 'internal', display: 'Visability', tag: 'radio', default: false, null: true, options: { true: 'internal', false: 'public' }, class: 'medium' },
|
||||
]
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class App.TicketArticleSender extends App.Model
|
||||
@configure 'TicketArticleSender', 'name'
|
||||
@configure 'TicketArticleSender', 'name', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: '/ticket_article_senders'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class App.TicketArticleType extends App.Model
|
||||
@configure 'TicketArticleType', 'name'
|
||||
@configure 'TicketArticleType', 'name', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: '/ticket_article_types'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class App.TicketPriority extends App.Model
|
||||
@configure 'TicketPriority', 'name', 'note', 'active'
|
||||
@configure 'TicketPriority', 'name', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/ticket_priorities'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class App.TicketStateType extends App.Model
|
||||
@configure 'TicketStateType', 'name', 'note', 'active'
|
||||
@configure 'TicketStateType', 'name', 'note', 'active', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: '/ticket_state_types'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class App.User extends App.Model
|
||||
@configure 'User', 'login', 'firstname', 'lastname', 'email', 'web', 'password', 'phone', 'fax', 'mobile', 'street', 'zip', 'city', 'country', 'organization_id', 'department', 'note', 'role_ids', 'group_ids', 'active', 'invite'
|
||||
@configure 'User', 'login', 'firstname', 'lastname', 'email', 'web', 'password', 'phone', 'fax', 'mobile', 'street', 'zip', 'city', 'country', 'organization_id', 'department', 'note', 'role_ids', 'group_ids', 'active', 'invite', 'updated_at'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: 'api/users'
|
||||
|
||||
|
|
29
app/models/observer/web_socket_notify.rb
Normal file
29
app/models/observer/web_socket_notify.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
require 'session'
|
||||
|
||||
class Observer::WebSocketNotify < ActiveRecord::Observer
|
||||
observe :ticket, :user, 'ticket::_article'
|
||||
|
||||
def after_create(record)
|
||||
|
||||
# return if we run import mode
|
||||
return if Setting.get('import_mode')
|
||||
|
||||
Session.broadcast(
|
||||
:event => record.class.name.downcase + ':created',
|
||||
:data => { :id => record.id, :updated_at => record.updated_at }
|
||||
)
|
||||
end
|
||||
|
||||
def after_update(record)
|
||||
|
||||
# return if we run import mode
|
||||
return if Setting.get('import_mode')
|
||||
puts "#{record.class.name.downcase} UPDATED " + record.updated_at.to_s
|
||||
Session.broadcast(
|
||||
:event => record.class.name.downcase + ':updated',
|
||||
:data => { :id => record.id, :updated_at => record.updated_at }
|
||||
)
|
||||
end
|
||||
end
|
|
@ -25,7 +25,7 @@ module Zammad
|
|||
|
||||
# Activate observers that should always be running.
|
||||
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
|
||||
config.active_record.observers =
|
||||
config.active_record.observers =
|
||||
'observer::_history',
|
||||
'observer::_ticket::_first_response',
|
||||
'observer::_ticket::_last_contact',
|
||||
|
@ -41,11 +41,8 @@ module Zammad
|
|||
'observer::_ticket::_notification',
|
||||
'observer::_tag::_ticket_history',
|
||||
'observer::_ticket::_reset_new_state',
|
||||
'observer::_ticket::_escalation_calculation'
|
||||
|
||||
# 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.
|
||||
# config.time_zone = 'Central Time (US & Canada)'
|
||||
'observer::_ticket::_escalation_calculation',
|
||||
'observer::_web_socket_notify'
|
||||
|
||||
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
||||
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
||||
|
|
|
@ -293,6 +293,16 @@ module Session
|
|||
return data
|
||||
end
|
||||
|
||||
def self.broadcast( data )
|
||||
|
||||
# list all current clients
|
||||
client_list = self.list
|
||||
client_list.each {|local_client_id, local_client|
|
||||
Session.send( local_client_id, data )
|
||||
}
|
||||
return true
|
||||
end
|
||||
|
||||
def self.destory( client_id )
|
||||
path = @path + '/' + client_id.to_s
|
||||
FileUtils.rm_rf path
|
||||
|
|
|
@ -24,10 +24,6 @@ require 'daemons'
|
|||
:i => Dir.pwd.to_s + '/tmp/pids/websocket.pid'
|
||||
}
|
||||
|
||||
if ARGV[0] != 'start' && ARGV[0] != 'stop'
|
||||
puts "Usage: websocket-server.rb start|stop [options]"
|
||||
exit;
|
||||
end
|
||||
tls_options = {}
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Usage: websocket-server.rb start|stop [options]"
|
||||
|
@ -58,6 +54,11 @@ OptionParser.new do |opts|
|
|||
end
|
||||
end.parse!
|
||||
|
||||
if ARGV[0] != 'start' && ARGV[0] != 'stop'
|
||||
puts "Usage: websocket-server.rb start|stop [options]"
|
||||
exit;
|
||||
end
|
||||
|
||||
puts "Starting websocket server on #{ @options[:b] }:#{ @options[:p] } (secure:#{ @options[:s].to_s },pid:#{@options[:i].to_s})"
|
||||
#puts options.inspect
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ class AgentTicketActionsLevel2Test < TestCase
|
|||
# change task and page title in first browser
|
||||
{
|
||||
:execute => 'wait',
|
||||
:value => 30,
|
||||
:value => 10,
|
||||
},
|
||||
{
|
||||
:where => :instance1,
|
||||
|
|
Loading…
Reference in a new issue