Moved to websockets.
This commit is contained in:
parent
e6f4bd021f
commit
b2209ff868
26 changed files with 868 additions and 403 deletions
3
Gemfile
3
Gemfile
|
@ -58,3 +58,6 @@ gem 'simple-rss'
|
|||
# To use debugger
|
||||
# gem 'ruby-debug'
|
||||
|
||||
# event machine
|
||||
gem 'eventmachine'
|
||||
gem 'em-websocket'
|
||||
|
|
|
@ -2,7 +2,8 @@ class App.Controller extends Spine.Controller
|
|||
|
||||
# add @title methode to set title
|
||||
title: (name) ->
|
||||
$('html head title').html( Config.product_name + ' - ' + Ti(name) )
|
||||
# $('html head title').html( Config.product_name + ' - ' + Ti(name) )
|
||||
document.title = Config.product_name + ' - ' + Ti(name)
|
||||
|
||||
# add @notify methode to create notification
|
||||
notify: (data) ->
|
||||
|
@ -545,6 +546,13 @@ class App.Controller extends Spine.Controller
|
|||
|
||||
return newInstance
|
||||
|
||||
clearInterval: (interval_id) =>
|
||||
# check global var
|
||||
if !@intervalID
|
||||
@intervalID = {}
|
||||
|
||||
clearInterval( @intervalID[interval_id] ) if @intervalID[interval_id]
|
||||
|
||||
interval: (action, interval, interval_id) =>
|
||||
|
||||
# check global var
|
||||
|
@ -778,11 +786,14 @@ class App.ControllerModal extends App.Controller
|
|||
super(options)
|
||||
|
||||
modalShow: (params) =>
|
||||
@el.modal({
|
||||
defaults = {
|
||||
backdrop: true,
|
||||
keyboard: true,
|
||||
show: true
|
||||
})
|
||||
show: true,
|
||||
}
|
||||
data = $.extend({}, defaults, params)
|
||||
@el.modal(data)
|
||||
|
||||
@el.bind('hidden', =>
|
||||
|
||||
# navigate back to home page
|
||||
|
@ -805,3 +816,17 @@ class App.ControllerModal extends App.Controller
|
|||
submit: (e) =>
|
||||
e.preventDefault()
|
||||
@log 'You need to implement your own "submit" method!'
|
||||
|
||||
class App.ErrorModal extends App.ControllerModal
|
||||
constructor: ->
|
||||
super
|
||||
@render()
|
||||
|
||||
render: ->
|
||||
@html App.view('error')(
|
||||
message: @message
|
||||
)
|
||||
@modalShow(
|
||||
backdrop: false,
|
||||
keyboard: false,
|
||||
)
|
||||
|
|
|
@ -9,25 +9,29 @@ class App.DashboardActivityStream extends App.Controller
|
|||
@items = []
|
||||
|
||||
# refresh list ever 140 sec.
|
||||
@interval( @fetch, 1400000, 'dashboard_activity_stream' )
|
||||
|
||||
# @interval( @fetch, 1400000, 'dashboard_activity_stream' )
|
||||
@fetch()
|
||||
Spine.bind 'activity_stream_rebuild', (data) =>
|
||||
@log 'a_stream', data
|
||||
@fetch()
|
||||
|
||||
fetch: =>
|
||||
|
||||
# use cache of first page
|
||||
if window.LastRefresh[ 'dashboard_activity_stream' ]
|
||||
@render( window.LastRefresh[ 'dashboard_activity_stream' ] )
|
||||
if window.LastRefresh[ 'activity_stream' ]
|
||||
@load( window.LastRefresh[ 'activity_stream' ] )
|
||||
|
||||
# get data
|
||||
App.Com.ajax(
|
||||
id: 'dashoard_activity_stream',
|
||||
type: 'GET',
|
||||
url: '/activity_stream',
|
||||
data: {
|
||||
limit: @limit,
|
||||
}
|
||||
processData: true,
|
||||
success: @load
|
||||
)
|
||||
# # get data
|
||||
# App.Com.ajax(
|
||||
# id: 'dashoard_activity_stream',
|
||||
# type: 'GET',
|
||||
# url: '/activity_stream',
|
||||
# data: {
|
||||
# limit: @limit,
|
||||
# }
|
||||
# processData: true,
|
||||
# success: @load
|
||||
# )
|
||||
|
||||
load: (data) =>
|
||||
items = data.activity_stream
|
||||
|
|
|
@ -3,36 +3,18 @@ $ = jQuery.sub()
|
|||
class App.DashboardRss extends App.Controller
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
|
||||
# refresh list ever 600 sec.
|
||||
@interval( @fetch, 6000000, 'dashboard_rss' )
|
||||
|
||||
fetch: =>
|
||||
# refresh list ever 600 sec.
|
||||
Spine.bind 'rss_rebuild', (data) =>
|
||||
@load(data)
|
||||
|
||||
# use cache of first page
|
||||
if window.LastRefresh[ 'dashboard_rss' ]
|
||||
@render( window.LastRefresh[ 'dashboard_rss' ] )
|
||||
|
||||
# get data
|
||||
App.Com.ajax(
|
||||
id: 'dashboard_rss',
|
||||
type: 'GET',
|
||||
url: '/rss_fetch',
|
||||
data: {
|
||||
limit: @limit,
|
||||
url: @url,
|
||||
}
|
||||
processData: true,
|
||||
success: @load
|
||||
)
|
||||
@load( window.LastRefresh[ 'dashboard_rss' ] )
|
||||
|
||||
load: (data) =>
|
||||
items = data.items || []
|
||||
|
||||
# set cache
|
||||
window.LastRefresh[ 'dashboard_rss' ] = items
|
||||
|
||||
@head = data.head || '?'
|
||||
@render(items)
|
||||
|
||||
render: (items) ->
|
||||
|
|
|
@ -271,7 +271,7 @@ class Index extends App.Controller
|
|||
@tickets = []
|
||||
|
||||
# rebuild navbar with updated ticket count of overviews
|
||||
Spine.trigger 'navupdate_remote'
|
||||
App.WebSocket.send( event: 'navupdate_ticket_overview' )
|
||||
|
||||
# fetch overview data again
|
||||
@fetch()
|
||||
|
|
|
@ -9,13 +9,12 @@ class Index extends App.Controller
|
|||
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
|
||||
# set title
|
||||
@title 'Get Started'
|
||||
@navupdate '#get_started'
|
||||
|
||||
|
||||
@master_user = 0
|
||||
|
||||
# @render()
|
||||
@fetch()
|
||||
|
||||
|
@ -43,7 +42,7 @@ class Index extends App.Controller
|
|||
)
|
||||
|
||||
render: ->
|
||||
|
||||
|
||||
# check authentication, redirect to login if master user already exists
|
||||
if !@master_user && !@authenticate()
|
||||
@navigate '#login'
|
||||
|
@ -59,7 +58,7 @@ class Index extends App.Controller
|
|||
submit: (e) ->
|
||||
e.preventDefault()
|
||||
@params = @formParam(e.target)
|
||||
|
||||
|
||||
# if no login is given, use emails as fallback
|
||||
if !@params.login && @params.email
|
||||
@params.login = @params.email
|
||||
|
@ -102,28 +101,24 @@ class Index extends App.Controller
|
|||
|
||||
# rerender page
|
||||
@render()
|
||||
|
||||
# error: =>
|
||||
# @modalHide()
|
||||
)
|
||||
|
||||
|
||||
relogin: (data, status, xhr) =>
|
||||
@log 'login:success', data
|
||||
|
||||
# login check
|
||||
App.Auth.loginCheck()
|
||||
|
||||
|
||||
# add notify
|
||||
Spine.trigger 'notify:removeall'
|
||||
# @notify
|
||||
# type: 'success',
|
||||
# msg: 'Thanks for joining. Email sent to "' + @params.email + '". Please verify your email address.'
|
||||
|
||||
|
||||
@el.find('.master_user').fadeOut('slow', =>
|
||||
@el.find('.agent_user').fadeIn()
|
||||
)
|
||||
|
||||
|
||||
|
||||
Config.Routes['getting_started'] = Index
|
||||
|
|
|
@ -3,7 +3,7 @@ $ = jQuery.sub()
|
|||
class Index extends App.Controller
|
||||
events:
|
||||
'submit #login': 'login',
|
||||
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
@title 'Sign in'
|
||||
|
@ -37,12 +37,12 @@ class Index extends App.Controller
|
|||
for key, provider of auth_provider_all
|
||||
if Config[provider.config] is true || Config[provider.config] is "true"
|
||||
auth_providers.push provider
|
||||
|
||||
|
||||
@html App.view('login')(
|
||||
item: data,
|
||||
auth_providers: auth_providers,
|
||||
)
|
||||
|
||||
|
||||
# set focus
|
||||
if !$(@el).find('[name="username"]').val()
|
||||
$(@el).find('[name="username"]').focus()
|
||||
|
@ -55,17 +55,17 @@ class Index extends App.Controller
|
|||
login: (e) ->
|
||||
e.preventDefault()
|
||||
params = @formParam(e.target)
|
||||
|
||||
|
||||
# remember username
|
||||
@username = params['username']
|
||||
|
||||
|
||||
# session create with login/password
|
||||
App.Auth.login(
|
||||
data: params,
|
||||
success: @success
|
||||
error: @error,
|
||||
)
|
||||
|
||||
|
||||
success: (data, status, xhr) =>
|
||||
@log 'login:success', data
|
||||
|
||||
|
@ -85,10 +85,14 @@ class Index extends App.Controller
|
|||
for key, value of data.default_collections
|
||||
App[key].refresh( value, options: { clear: true } )
|
||||
|
||||
# rebuild navbar with user data
|
||||
Spine.trigger 'navrebuild', data.session
|
||||
|
||||
# rebuild navbar with updated ticket count of overviews
|
||||
Spine.trigger 'navupdate_remote'
|
||||
# update websocked auth info
|
||||
App.WebSocket.auth()
|
||||
|
||||
# rebuild navbar with ticket overview counter
|
||||
App.WebSocket.send( event: 'navupdate_ticket_overview' )
|
||||
|
||||
# add notify
|
||||
Spine.trigger 'notify:removeall'
|
||||
|
@ -100,7 +104,7 @@ class Index extends App.Controller
|
|||
# redirect to #
|
||||
if window.Config['requested_url'] isnt ''
|
||||
@navigate window.Config['requested_url']
|
||||
|
||||
|
||||
# reset
|
||||
window.Config['requested_url'] = ''
|
||||
else
|
||||
|
@ -108,14 +112,14 @@ class Index extends App.Controller
|
|||
|
||||
error: (xhr, statusText, error) =>
|
||||
console.log 'login:error'
|
||||
|
||||
|
||||
# add notify
|
||||
Spine.trigger 'notify:removeall'
|
||||
Spine.trigger 'notify', {
|
||||
type: 'error',
|
||||
msg: T('Wrong Username and Password combination.'),
|
||||
}
|
||||
|
||||
|
||||
# rerender login page
|
||||
@render(
|
||||
username: @username
|
||||
|
|
|
@ -4,11 +4,10 @@ class Index extends Spine.Controller
|
|||
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
@signout()
|
||||
|
||||
|
||||
signout: ->
|
||||
|
||||
|
||||
# remove remote session
|
||||
App.Auth.logout()
|
||||
|
||||
|
@ -17,7 +16,7 @@ class Index extends Spine.Controller
|
|||
window.Session = {}
|
||||
@log 'Session', window.Session
|
||||
Spine.trigger 'navrebuild'
|
||||
|
||||
|
||||
# redirect to login
|
||||
@navigate 'login'
|
||||
|
||||
|
|
|
@ -5,36 +5,47 @@ class App.Navigation extends App.Controller
|
|||
super
|
||||
@log 'nav...'
|
||||
@render()
|
||||
|
||||
sync_ticket_overview = =>
|
||||
@interval( @ticket_overview, 30000, 'nav_ticket_overview' )
|
||||
|
||||
sync_recent_viewed = =>
|
||||
@interval( @recent_viewed, 40000, 'nav_recent_viewed' )
|
||||
|
||||
# update selected item
|
||||
Spine.bind 'navupdate', (data) =>
|
||||
@update(arguments[0])
|
||||
|
||||
|
||||
# rebuild nav bar with given user data
|
||||
Spine.bind 'navrebuild', (user) =>
|
||||
@log 'navbarrebuild', user
|
||||
@render(user)
|
||||
|
||||
Spine.bind 'navupdate_remote', (user) =>
|
||||
@log 'navupdate_remote'
|
||||
@delay( sync_ticket_overview, 500 )
|
||||
@delay( sync_recent_viewed, 1000 )
|
||||
|
||||
# rerender if new overview data is there
|
||||
@delay( sync_ticket_overview, 800 )
|
||||
@delay( sync_recent_viewed, 1000 )
|
||||
|
||||
# rebuild ticket overview data
|
||||
Spine.bind 'navupdate_ticket_overview', (data) =>
|
||||
@ticket_overview_build(data)
|
||||
|
||||
# rebuild recent viewd data
|
||||
Spine.bind 'update_recent_viewed', (data) =>
|
||||
@recent_viewed_build(data)
|
||||
|
||||
render: (user) ->
|
||||
nav_left = @getItems( navbar: Config.NavBar )
|
||||
nav_right = @getItems( navbar: Config.NavBarRight )
|
||||
|
||||
# get open tabs to repopen on rerender
|
||||
open_tab = {}
|
||||
@el.find('.open').children('a').each( (i,d) =>
|
||||
href = $(d).attr('href')
|
||||
open_tab[href] = true
|
||||
)
|
||||
|
||||
# get active tabs to reactivate on rerender
|
||||
active_tab = {}
|
||||
@el.find('.active').children('a').each( (i,d) =>
|
||||
href = $(d).attr('href')
|
||||
active_tab[href] = true
|
||||
)
|
||||
|
||||
@html App.view('navigation')(
|
||||
navbar_left: nav_left,
|
||||
navbar_right: nav_right,
|
||||
open_tab: open_tab,
|
||||
active_tab: active_tab,
|
||||
user: user,
|
||||
)
|
||||
|
||||
|
@ -119,118 +130,65 @@ class App.Navigation extends App.Controller
|
|||
|
||||
update: (url) =>
|
||||
@el.find('li').removeClass('active')
|
||||
# if url isnt '#'
|
||||
@el.find("[href=\"#{url}\"]").parents('li').addClass('active')
|
||||
# @el.find("[href*=\"#{url}\"]").parents('li').addClass('active')
|
||||
|
||||
# get data
|
||||
ticket_overview: =>
|
||||
ticket_overview_build: (data) =>
|
||||
|
||||
# do no load and rerender if sub-menu is open
|
||||
open = @el.find('.open').val()
|
||||
if open isnt undefined
|
||||
return
|
||||
|
||||
# do no load and rerender if user is not logged in
|
||||
if !window.Session['id']
|
||||
return
|
||||
# remove old views
|
||||
for key of Config.NavBar
|
||||
if Config.NavBar[key].parent is '#ticket/view'
|
||||
delete Config.NavBar[key]
|
||||
|
||||
# only of lod request is already done
|
||||
# add new views
|
||||
for item in data
|
||||
Config.NavBar['TicketOverview' + item.url] = {
|
||||
prio: item.prio,
|
||||
parent: '#ticket/view',
|
||||
name: item.name,
|
||||
count: item.count,
|
||||
target: '#ticket/view/' + item.url,
|
||||
role: ['Agent'],
|
||||
}
|
||||
|
||||
if !@req_overview
|
||||
@req_overview = App.Com.ajax(
|
||||
id: 'navbar_ticket_overviews',
|
||||
type: 'GET',
|
||||
url: '/ticket_overviews',
|
||||
data: {},
|
||||
processData: true,
|
||||
success: (data, status, xhr) =>
|
||||
|
||||
# remove old views
|
||||
for key of Config.NavBar
|
||||
if Config.NavBar[key].parent is '#ticket/view'
|
||||
delete Config.NavBar[key]
|
||||
|
||||
# add new views
|
||||
for item in data
|
||||
Config.NavBar['TicketOverview' + item.url] = {
|
||||
prio: item.prio,
|
||||
parent: '#ticket/view',
|
||||
name: item.name,
|
||||
count: item.count,
|
||||
target: '#ticket/view/' + item.url,
|
||||
role: ['Agent'],
|
||||
}
|
||||
|
||||
# rebuild navbar
|
||||
Spine.trigger 'navrebuild', window.Session
|
||||
# rebuild navbar
|
||||
Spine.trigger 'navrebuild', window.Session
|
||||
|
||||
# reset ajax call
|
||||
@req_overview = undefined
|
||||
)
|
||||
recent_viewed_build: (data) =>
|
||||
|
||||
# get data
|
||||
recent_viewed: =>
|
||||
items = data.recent_viewed
|
||||
|
||||
# do no load and rerender if sub-menu is open
|
||||
open = @el.find('.open').val()
|
||||
if open isnt undefined
|
||||
return
|
||||
|
||||
# do no load and rerender if user is not logged in
|
||||
if !window.Session['id']
|
||||
return
|
||||
# load user collection
|
||||
@loadCollection( type: 'User', data: data.users )
|
||||
|
||||
# only of lod request is already done
|
||||
if !@req_recent_viewed
|
||||
@req_recent_viewed = App.Com.ajax(
|
||||
id: 'navbar_recent_viewed',
|
||||
type: 'GET',
|
||||
url: '/recent_viewed',
|
||||
data: {
|
||||
limit: 5,
|
||||
}
|
||||
processData: true,
|
||||
success: (data, status, xhr) =>
|
||||
|
||||
items = data.recent_viewed
|
||||
|
||||
# load user collection
|
||||
@loadCollection( type: 'User', data: data.users )
|
||||
|
||||
# load ticket collection
|
||||
@loadCollection( type: 'Ticket', data: data.tickets )
|
||||
|
||||
# remove old views
|
||||
for key of Config.NavBarRight
|
||||
if Config.NavBarRight[key].parent is '#current_user'
|
||||
part = Config.NavBarRight[key].target.split '::'
|
||||
if part is 'RecendViewed'
|
||||
delete Config.NavBarRight[key]
|
||||
|
||||
# add new views
|
||||
prio = 5000
|
||||
for item in items
|
||||
divider = false
|
||||
navheader = false
|
||||
if prio is 5000
|
||||
divider = true
|
||||
navheader = 'Recent Viewed'
|
||||
ticket = App.Ticket.find(item.o_id)
|
||||
prio++
|
||||
Config.NavBarRight['RecendViewed::' + ticket.id] = {
|
||||
prio: prio,
|
||||
parent: '#current_user',
|
||||
name: item.history_object.name + ' (' + ticket.title + ')',
|
||||
target: '#ticket/zoom/' + ticket.id,
|
||||
role: ['Agent'],
|
||||
divider: divider,
|
||||
navheader: navheader
|
||||
}
|
||||
|
||||
# rebuild navbar
|
||||
Spine.trigger 'navrebuild', window.Session
|
||||
# load ticket collection
|
||||
@loadCollection( type: 'Ticket', data: data.tickets )
|
||||
|
||||
# reset ajax call
|
||||
@req_recent_viewed = undefined
|
||||
)
|
||||
# remove old views
|
||||
for key of Config.NavBarRight
|
||||
if Config.NavBarRight[key].parent is '#current_user'
|
||||
part = key.split '::'
|
||||
if part[0] is 'RecendViewed'
|
||||
delete Config.NavBarRight[key]
|
||||
|
||||
# add new views
|
||||
prio = 8000
|
||||
for item in items
|
||||
divider = false
|
||||
navheader = false
|
||||
if prio is 8000
|
||||
divider = true
|
||||
navheader = 'Recent Viewed'
|
||||
ticket = App.Ticket.find(item.o_id)
|
||||
prio++
|
||||
Config.NavBarRight['RecendViewed::' + ticket.id + '-' + prio ] = {
|
||||
prio: prio,
|
||||
parent: '#current_user',
|
||||
name: item.history_object.name + ' (' + ticket.title + ')',
|
||||
target: '#ticket/zoom/' + ticket.id,
|
||||
role: ['Agent'],
|
||||
divider: divider,
|
||||
navheader: navheader
|
||||
}
|
||||
|
||||
# rebuild navbar
|
||||
Spine.trigger 'navrebuild', window.Session
|
|
@ -11,6 +11,7 @@
|
|||
#= require ./lib/bootstrap-popover.js
|
||||
#= require ./lib/bootstrap-modal.js
|
||||
#= require ./lib/bootstrap-tab.js
|
||||
#= require ./lib/bootstrap-transition.js
|
||||
|
||||
#= require ./lib/underscore.coffee
|
||||
#= require ./lib/ba-linkify.js
|
||||
|
@ -23,6 +24,7 @@
|
|||
#not_used= require_tree ./lib
|
||||
#= require_self
|
||||
#= require ./lib/ajax.js.coffee
|
||||
#= require ./lib/websocket.js.coffee
|
||||
#= require ./lib/auth.js.coffee
|
||||
#= require ./lib/i18n.js.coffee
|
||||
#= require_tree ./models
|
||||
|
@ -57,13 +59,15 @@ Config2.set( 'a', 123)
|
|||
console.log '1112222', Config2.get( 'a')
|
||||
###
|
||||
|
||||
|
||||
class App.Run extends Spine.Controller
|
||||
constructor: ->
|
||||
super
|
||||
@log 'RUN app'#, @
|
||||
@log 'RUN app'
|
||||
@el = $('#app')
|
||||
|
||||
# create web socket connection
|
||||
App.WebSocket.connect()
|
||||
|
||||
# init of i18n
|
||||
new App.i18n
|
||||
|
||||
|
@ -84,27 +88,6 @@ class App.Run extends Spine.Controller
|
|||
window.Session['UISelection'] = @getSelected() + ''
|
||||
)
|
||||
|
||||
# @ws = new WebSocket("ws://localhost:3001/");
|
||||
|
||||
# Set event handlers.
|
||||
# @ws.onopen = ->
|
||||
# console.log("onopen")
|
||||
|
||||
# @ws.onmessage = (e) ->
|
||||
# e.data contains received string.
|
||||
# console.log("onmessage: " + e.data)
|
||||
# eval e.data
|
||||
|
||||
# Spine.bind 'ws:send', (data) =>
|
||||
# @log 'ws:send', data
|
||||
# @ws.send(data);
|
||||
|
||||
# @ws.onclose = ->
|
||||
# console.log("onclose")
|
||||
|
||||
# @ws.onerror = ->
|
||||
# console.log("onerror")
|
||||
|
||||
getSelected: ->
|
||||
text = '';
|
||||
if window.getSelection
|
||||
|
@ -125,24 +108,35 @@ class App.Content extends Spine.Controller
|
|||
for route, callback of Config.Routes
|
||||
do (route, callback) =>
|
||||
@route(route, (params) ->
|
||||
|
||||
# remember current controller
|
||||
Config['ActiveController'] = route
|
||||
Spine.trigger( 'ws:send', JSON.stringify( { action: 'active_controller', controller: route, params: params } ) )
|
||||
|
||||
# send current controller
|
||||
params_only = {}
|
||||
for i of params
|
||||
if typeof params[i] isnt 'object'
|
||||
params_only[i] = params[i]
|
||||
App.WebSocket.send(
|
||||
action: 'active_controller',
|
||||
controller: route,
|
||||
params: params_only,
|
||||
)
|
||||
|
||||
# unbind in controller area
|
||||
@el.unbind()
|
||||
@el.undelegate()
|
||||
|
||||
|
||||
# remove waypoints
|
||||
$('footer').waypoint('remove')
|
||||
|
||||
|
||||
params.el = @el
|
||||
params.auth = @auth
|
||||
new callback( params )
|
||||
|
||||
# scroll to top
|
||||
# window.scrollTo(0,0)
|
||||
)
|
||||
|
||||
Spine.Route.setup()
|
||||
Spine.Route.setup()
|
||||
|
||||
window.App = App
|
|
@ -34,6 +34,9 @@ class App.Auth
|
|||
# empty session
|
||||
window.Session = {}
|
||||
|
||||
# update websocked auth info
|
||||
App.WebSocket.auth()
|
||||
|
||||
# rebuild navbar with new navbar items
|
||||
Spine.trigger 'navrebuild'
|
||||
|
||||
|
@ -50,6 +53,9 @@ class App.Auth
|
|||
# store user data
|
||||
for key, value of data.session
|
||||
window.Session[key] = value
|
||||
|
||||
# update websocked auth info
|
||||
App.WebSocket.auth()
|
||||
|
||||
# refresh/load default collections
|
||||
for key, value of data.default_collections
|
||||
|
@ -61,12 +67,14 @@ class App.Auth
|
|||
# rebuild navbar with updated ticket count of overviews
|
||||
Spine.trigger 'navupdate_remote'
|
||||
|
||||
|
||||
error: (xhr, statusText, error) =>
|
||||
console.log 'loginCheck:error'#, error, statusText, xhr.statusCode
|
||||
|
||||
|
||||
# empty session
|
||||
window.Session = {}
|
||||
|
||||
# update websocked auth info
|
||||
App.WebSocket.auth()
|
||||
)
|
||||
|
||||
@logout: ->
|
||||
|
@ -75,4 +83,13 @@ class App.Auth
|
|||
id: 'logout',
|
||||
type: 'DELETE',
|
||||
url: '/signout',
|
||||
success: =>
|
||||
|
||||
# update websocked auth info
|
||||
App.WebSocket.auth()
|
||||
|
||||
error: (xhr, statusText, error) =>
|
||||
|
||||
# update websocked auth info
|
||||
App.WebSocket.auth()
|
||||
)
|
61
app/assets/javascripts/app/lib/bootstrap-transition.js
vendored
Normal file
61
app/assets/javascripts/app/lib/bootstrap-transition.js
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* ===================================================
|
||||
* bootstrap-transition.js v2.0.4
|
||||
* http://twitter.github.com/bootstrap/javascript.html#transitions
|
||||
* ===================================================
|
||||
* Copyright 2012 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ========================================================== */
|
||||
|
||||
|
||||
!function ($) {
|
||||
|
||||
$(function () {
|
||||
|
||||
"use strict"; // jshint ;_;
|
||||
|
||||
|
||||
/* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
|
||||
* ======================================================= */
|
||||
|
||||
$.support.transition = (function () {
|
||||
|
||||
var transitionEnd = (function () {
|
||||
|
||||
var el = document.createElement('bootstrap')
|
||||
, transEndEventNames = {
|
||||
'WebkitTransition' : 'webkitTransitionEnd'
|
||||
, 'MozTransition' : 'transitionend'
|
||||
, 'OTransition' : 'oTransitionEnd'
|
||||
, 'msTransition' : 'MSTransitionEnd'
|
||||
, 'transition' : 'transitionend'
|
||||
}
|
||||
, name
|
||||
|
||||
for (name in transEndEventNames){
|
||||
if (el.style[name] !== undefined) {
|
||||
return transEndEventNames[name]
|
||||
}
|
||||
}
|
||||
|
||||
}())
|
||||
|
||||
return transitionEnd && {
|
||||
end: transitionEnd
|
||||
}
|
||||
|
||||
})()
|
||||
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
114
app/assets/javascripts/app/lib/websocket.js.coffee
Normal file
114
app/assets/javascripts/app/lib/websocket.js.coffee
Normal file
|
@ -0,0 +1,114 @@
|
|||
$ = jQuery.sub()
|
||||
|
||||
class App.WebSocket
|
||||
_instance = undefined # Must be declared here to force the closure on the class
|
||||
@connect: (args) -> # Must be a static method
|
||||
if _instance == undefined
|
||||
_instance ?= new _Singleton
|
||||
_instance
|
||||
|
||||
@send: (args) -> # Must be a static method
|
||||
@connect()
|
||||
_instance.send(args)
|
||||
|
||||
@auth: (args) -> # Must be a static method
|
||||
@connect()
|
||||
_instance.auth(args)
|
||||
|
||||
# The actual Singleton class
|
||||
class _Singleton extends Spine.Controller
|
||||
queue: []
|
||||
|
||||
constructor: (@args) ->
|
||||
@connect()
|
||||
|
||||
send: (data) =>
|
||||
console.log 'ws:send trying', data, @ws, @ws.readyState
|
||||
|
||||
# A value of 0 indicates that the connection has not yet been established.
|
||||
# A value of 1 indicates that the connection is established and communication is possible.
|
||||
# A value of 2 indicates that the connection is going through the closing handshake.
|
||||
# A value of 3 indicates that the connection has been closed or could not be opened.
|
||||
if @ws.readyState is 0
|
||||
@queue.push data
|
||||
else
|
||||
console.log( 'ws:send', data )
|
||||
string = JSON.stringify( data )
|
||||
@ws.send(string)
|
||||
|
||||
auth: (data) =>
|
||||
|
||||
# logon websocket
|
||||
data = {
|
||||
action: 'login',
|
||||
session: window.Session
|
||||
}
|
||||
@send(data)
|
||||
|
||||
close: =>
|
||||
@ws.close()
|
||||
|
||||
connect: =>
|
||||
# console.log '------------ws connect....--------------'
|
||||
|
||||
if !window.WebSocket
|
||||
@error = new App.ErrorModal(
|
||||
message: 'Sorry, no websocket support!'
|
||||
)
|
||||
return
|
||||
|
||||
@ws = new window.WebSocket( "ws://" + window.location.hostname + ":6042/" )
|
||||
|
||||
# Set event handlers.
|
||||
@ws.onopen = =>
|
||||
console.log( "onopen" )
|
||||
|
||||
# close error message if exists
|
||||
if @error
|
||||
@error.modalHide()
|
||||
@error = undefined
|
||||
|
||||
@auth()
|
||||
|
||||
# empty queue
|
||||
for item in @queue
|
||||
console.log( 'ws:send queue', item )
|
||||
@send(item)
|
||||
@queue = []
|
||||
|
||||
@ws.onmessage = (e) ->
|
||||
pipe = JSON.parse( e.data )
|
||||
# console.log( "ws:onmessage", pipe )
|
||||
|
||||
# go through all blocks
|
||||
for item in pipe
|
||||
|
||||
# fill collection
|
||||
if item['collection']
|
||||
console.log( "ws:onmessage collection:" + item['collection'] )
|
||||
window.LastRefresh[ item['collection'] ] = item['data']
|
||||
|
||||
# fire event
|
||||
if item['event']
|
||||
console.log( "ws:onmessage event:" + item['event'] )
|
||||
Spine.trigger( item['event'], item['data'] )
|
||||
|
||||
# bind to send messages
|
||||
Spine.bind 'ws:send', (data) =>
|
||||
@send(data)
|
||||
|
||||
@ws.onclose = (e) =>
|
||||
console.log( "onclose", e )
|
||||
|
||||
# show error message
|
||||
if !@error
|
||||
@error = new App.ErrorModal(
|
||||
message: 'No connection to websocket, trying to reconnect...'
|
||||
)
|
||||
|
||||
# try reconnect after 5 sec.
|
||||
@delay @connect, 5000
|
||||
|
||||
@ws.onerror = ->
|
||||
console.log( "onerror" )
|
||||
|
17
app/assets/javascripts/app/views/error.jst.eco
Normal file
17
app/assets/javascripts/app/views/error.jst.eco
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div class="modal-header">
|
||||
<!--
|
||||
<a href="#" class="close">×</a>
|
||||
-->
|
||||
<h3><%- T('Error') %></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal">
|
||||
<p><%= @message %>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<!--
|
||||
<button class="btn btn-primary submit"><%- T('Submit') %></button>
|
||||
<button class="btn cancel"><%- T('Cancel') %></button>
|
||||
-->
|
||||
</div>
|
|
@ -5,7 +5,7 @@
|
|||
<ul class="nav">
|
||||
<% for item in @navbar_left: %>
|
||||
<% if item.child: %>
|
||||
<li class="dropdown">
|
||||
<li class="dropdown <% if @open_tab[item.target] : %>open<% end %>">
|
||||
<a href="<%= item.target %>" class="dropdown-toggle" data-toggle="dropdown"><%- T(item.name) %> <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<% for item in item.child: %>
|
||||
|
@ -15,12 +15,12 @@
|
|||
<% if item.navheader: %>
|
||||
<li class="nav-header"><%- T(item.navheader) %></li>
|
||||
<% end %>
|
||||
<li><a href="<%= item.target %>"><%- T(item.name) %><% if item['count'] isnt undefined: %> <span class="count">(<%= item['count'] %>)</span><% end %></a></li>
|
||||
<li class="<% if @active_tab[item.target] : %>active<% end %>"><a href="<%= item.target %>"><%- T(item.name) %><% if item['count'] isnt undefined: %> <span class="count">(<%= item['count'] %>)</span><% end %></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</li>
|
||||
<% else: %>
|
||||
<li><a href="<%= item.target %>"><%- T(item.name) %></a></li>
|
||||
<li class="<% if @active_tab[item.target] : %>active<% end %>"><a href="<%= item.target %>"><%- T(item.name) %></a></li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<ul class="nav pull-right">
|
||||
<% for item in @navbar_right: %>
|
||||
<% if item.child: %>
|
||||
<li class="dropdown">
|
||||
<li class="dropdown <% if @open_tab[item.target] : %>open<% end %>">
|
||||
<a href="<%= item.target %>" class="dropdown-toggle" data-toggle="dropdown"><%- T(item.name) %> <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<% for item in item.child: %>
|
||||
|
@ -42,12 +42,12 @@
|
|||
<% if item.navheader: %>
|
||||
<li class="nav-header"><%- T(item.navheader) %></li>
|
||||
<% end %>
|
||||
<li><a href="<%= item.target %>"><%- T(item.name) %><% if item['count'] isnt undefined: %> <span class="count">(<%= item['count'] %>)</span><% end %></a></li>
|
||||
<li class="<% if @active_tab[item.target] : %>active<% end %>"><a href="<%= item.target %>"><%- T(item.name) %><% if item['count'] isnt undefined: %> <span class="count">(<%= item['count'] %>)</span><% end %></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</li>
|
||||
<% else: %>
|
||||
<li><a href="<%= item.target %>"><%- T(item.name) %></a></li>
|
||||
<li class="<% if @active_tab[item.target] : %>active<% end %>"><a href="<%= item.target %>"><%- T(item.name) %></a></li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
|
@ -3,56 +3,10 @@ class ActivityController < ApplicationController
|
|||
|
||||
# GET /activity_stream
|
||||
def activity_stream
|
||||
activity_stream = History.activity_stream(current_user, params[:limit])
|
||||
|
||||
# get related users
|
||||
users = {}
|
||||
tickets = []
|
||||
articles = []
|
||||
activity_stream.each {|item|
|
||||
|
||||
# load article ids
|
||||
if item['history_object'] == 'Ticket'
|
||||
ticket = Ticket.find( item['o_id'] ).attributes
|
||||
tickets.push ticket
|
||||
|
||||
# load users
|
||||
if !users[ ticket['owner_id'] ]
|
||||
users[ ticket['owner_id'] ] = user_data_full( ticket['owner_id'] )
|
||||
end
|
||||
if !users[ ticket['customer_id'] ]
|
||||
users[ ticket['customer_id'] ] = user_data_full( ticket['customer_id'] )
|
||||
end
|
||||
end
|
||||
if item['history_object'] == 'Ticket::Article'
|
||||
article = Ticket::Article.find( item['o_id'] ).attributes
|
||||
if !article['subject'] || article['subject'] == ''
|
||||
article['subject'] = Ticket.find( article['ticket_id'] ).title
|
||||
end
|
||||
articles.push article
|
||||
|
||||
# load users
|
||||
if !users[ article['created_by_id'] ]
|
||||
users[ article['created_by_id'] ] = user_data_full( article['created_by_id'] )
|
||||
end
|
||||
end
|
||||
if item['history_object'] == 'User'
|
||||
users[ item['o_id'] ] = user_data_full( item['o_id'] )
|
||||
end
|
||||
|
||||
# load users
|
||||
if !users[ item['created_by_id'] ]
|
||||
users[ item['created_by_id'] ] = user_data_full( item['created_by_id'] )
|
||||
end
|
||||
}
|
||||
activity_stream = History.activity_stream_fulldata(current_user, params[:limit])
|
||||
|
||||
# return result
|
||||
render :json => {
|
||||
:activity_stream => activity_stream,
|
||||
:tickets => tickets,
|
||||
:articles => articles,
|
||||
:users => users,
|
||||
}
|
||||
render :json => activity_stream
|
||||
end
|
||||
|
||||
end
|
|
@ -161,52 +161,7 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def user_data_full (user_id)
|
||||
|
||||
# get user
|
||||
user = User.find_fulldata(user_id)
|
||||
|
||||
# do not show password
|
||||
user['password'] = ''
|
||||
|
||||
|
||||
# TEMP: compat. reasons
|
||||
user['preferences'] = {} if user['preferences'] == nil
|
||||
|
||||
items = []
|
||||
if user['preferences'][:tickets_open].to_i > 0
|
||||
item = {
|
||||
:url => '',
|
||||
:name => 'open',
|
||||
:count => user['preferences'][:tickets_open] || 0,
|
||||
:title => 'Open Tickets',
|
||||
:class => 'user-tickets',
|
||||
:data => 'open'
|
||||
}
|
||||
items.push item
|
||||
end
|
||||
if user['preferences'][:tickets_closed].to_i > 0
|
||||
item = {
|
||||
:url => '',
|
||||
:name => 'closed',
|
||||
:count => user['preferences'][:tickets_closed] || 0,
|
||||
:title => 'Closed Tickets',
|
||||
:class => 'user-tickets',
|
||||
:data => 'closed'
|
||||
}
|
||||
items.push item
|
||||
end
|
||||
|
||||
# show linked topics and items
|
||||
if items.count > 0
|
||||
topic = {
|
||||
:title => 'Tickets',
|
||||
:items => items,
|
||||
}
|
||||
user['links'] = []
|
||||
user['links'].push topic
|
||||
end
|
||||
|
||||
return user
|
||||
user = User.user_data_full(user_id)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -3,36 +3,10 @@ class RecentViewedController < ApplicationController
|
|||
|
||||
# GET /recent_viewed
|
||||
def recent_viewed
|
||||
recent_viewed = History.recent_viewed(current_user)
|
||||
|
||||
# get related users
|
||||
users = {}
|
||||
tickets = []
|
||||
recent_viewed.each {|item|
|
||||
|
||||
# load article ids
|
||||
# if item.history_object == 'Ticket'
|
||||
tickets.push Ticket.find( item['o_id'] ).attributes
|
||||
# end
|
||||
# if item.history_object 'Ticket::Article'
|
||||
# tickets.push Ticket::Article.find(item.o_id)
|
||||
# end
|
||||
# if item.history_object 'User'
|
||||
# tickets.push User.find(item.o_id)
|
||||
# end
|
||||
|
||||
# load users
|
||||
if !users[ item['created_by_id'] ]
|
||||
users[ item['created_by_id'] ] = user_data_full( item['created_by_id'] )
|
||||
end
|
||||
}
|
||||
recent_viewed = History.recent_viewed_fulldata(current_user)
|
||||
|
||||
# return result
|
||||
render :json => {
|
||||
:recent_viewed => recent_viewed,
|
||||
:tickets => tickets,
|
||||
:users => users,
|
||||
}
|
||||
render :json => recent_viewed
|
||||
end
|
||||
|
||||
end
|
|
@ -3,33 +3,9 @@ class RssController < ApplicationController
|
|||
|
||||
# GET /rss_fetch
|
||||
def fetch
|
||||
url = params[:url]
|
||||
limit = params[:limit] || 10
|
||||
|
||||
cache_key = 'rss::' + url
|
||||
items = Rails.cache.read( cache_key )
|
||||
if !items
|
||||
response = Net::HTTP.get_response( URI.parse(url) )
|
||||
if response.code.to_s != '200'
|
||||
render :json => { :message => "failed to fetch #{url}, code: #{response.code}"}, :status => :unprocessable_entity
|
||||
return
|
||||
end
|
||||
rss = SimpleRSS.parse response.body
|
||||
items = []
|
||||
fetched = 0
|
||||
rss.items.each { |item|
|
||||
record = {
|
||||
:id => item.id,
|
||||
:title => item.title,
|
||||
:summary => item.summary,
|
||||
:link => item.link,
|
||||
:published => item.published
|
||||
}
|
||||
items.push record
|
||||
fetched += 1
|
||||
break item if fetched == limit.to_i
|
||||
}
|
||||
Rails.cache.write( cache_key, items, :expires_in => 4.hours )
|
||||
items = RSS.fetch(params[:url], params[:limit])
|
||||
if items == nil
|
||||
render :json => { :message => "failed to fetch #{ params[:url] }", :status => :unprocessable_entity }
|
||||
end
|
||||
render :json => { :items => items }
|
||||
end
|
||||
|
|
|
@ -136,14 +136,65 @@ class History < ActiveRecord::Base
|
|||
end
|
||||
return datas
|
||||
end
|
||||
def self.activity_stream_fulldata(user, limit = 10)
|
||||
activity_stream = History.activity_stream( user, limit )
|
||||
|
||||
# get related users
|
||||
users = {}
|
||||
tickets = []
|
||||
articles = []
|
||||
activity_stream.each {|item|
|
||||
|
||||
# load article ids
|
||||
if item['history_object'] == 'Ticket'
|
||||
ticket = Ticket.find( item['o_id'] ).attributes
|
||||
tickets.push ticket
|
||||
|
||||
# load users
|
||||
if !users[ ticket['owner_id'] ]
|
||||
users[ ticket['owner_id'] ] = User.user_data_full( ticket['owner_id'] )
|
||||
end
|
||||
if !users[ ticket['customer_id'] ]
|
||||
users[ ticket['customer_id'] ] = User.user_data_full( ticket['customer_id'] )
|
||||
end
|
||||
end
|
||||
if item['history_object'] == 'Ticket::Article'
|
||||
article = Ticket::Article.find( item['o_id'] ).attributes
|
||||
if !article['subject'] || article['subject'] == ''
|
||||
article['subject'] = Ticket.find( article['ticket_id'] ).title
|
||||
end
|
||||
articles.push article
|
||||
|
||||
# load users
|
||||
if !users[ article['created_by_id'] ]
|
||||
users[ article['created_by_id'] ] = User.user_data_full( article['created_by_id'] )
|
||||
end
|
||||
end
|
||||
if item['history_object'] == 'User'
|
||||
users[ item['o_id'] ] = User.user_data_full( item['o_id'] )
|
||||
end
|
||||
|
||||
# load users
|
||||
if !users[ item['created_by_id'] ]
|
||||
users[ item['created_by_id'] ] = User.user_data_full( item['created_by_id'] )
|
||||
end
|
||||
}
|
||||
|
||||
return {
|
||||
:activity_stream => activity_stream,
|
||||
:tickets => tickets,
|
||||
:articles => articles,
|
||||
:users => users,
|
||||
}
|
||||
end
|
||||
|
||||
def self.recent_viewed(user)
|
||||
# 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").
|
||||
stream = History.select("distinct(o_id), created_by_id, history_type_id, history_object_id, created_at").
|
||||
where( :history_object_id => History::Object.where( :name => 'Ticket').first.id ).
|
||||
where( :history_type_id => History::Type.where( :name => ['viewed']) ).
|
||||
where( :history_type_id => History::Type.where( :name => ['viewed'] ) ).
|
||||
where( :created_by_id => user.id ).
|
||||
order('created_at DESC, id DESC').
|
||||
order('created_at DESC, id ASC').
|
||||
limit(10)
|
||||
datas = []
|
||||
stream.each do |item|
|
||||
|
@ -153,9 +204,49 @@ class History < ActiveRecord::Base
|
|||
datas.push data
|
||||
# item['history_attribute'] = item.history_attribute
|
||||
end
|
||||
# puts 'pppppppppp'
|
||||
# puts datas.inspect
|
||||
return datas
|
||||
end
|
||||
|
||||
def self.recent_viewed_fulldata(user)
|
||||
recent_viewed = History.recent_viewed(user)
|
||||
|
||||
# get related users
|
||||
users = {}
|
||||
tickets = []
|
||||
recent_viewed.each {|item|
|
||||
|
||||
# load article ids
|
||||
# if item.history_object == 'Ticket'
|
||||
ticket = Ticket.find( item['o_id'] ).attributes
|
||||
tickets.push ticket
|
||||
# end
|
||||
# if item.history_object 'Ticket::Article'
|
||||
# tickets.push Ticket::Article.find(item.o_id)
|
||||
# end
|
||||
# if item.history_object 'User'
|
||||
# tickets.push User.find(item.o_id)
|
||||
# end
|
||||
|
||||
# load users
|
||||
if !users[ ticket['owner_id'] ]
|
||||
users[ ticket['owner_id'] ] = User.user_data_full( ticket['owner_id'] )
|
||||
end
|
||||
if !users[ ticket['created_by_id'] ]
|
||||
users[ ticket['created_by_id'] ] = User.user_data_full( ticket['created_by_id'] )
|
||||
end
|
||||
if !users[ item['created_by_id'] ]
|
||||
users[ item['created_by_id'] ] = User.user_data_full( item['created_by_id'] )
|
||||
end
|
||||
}
|
||||
return {
|
||||
:recent_viewed => recent_viewed,
|
||||
:tickets => tickets,
|
||||
:users => users,
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
def check_type
|
||||
puts '--------------'
|
||||
|
|
|
@ -123,7 +123,6 @@ class Ticket < ActiveRecord::Base
|
|||
return subject
|
||||
end
|
||||
|
||||
|
||||
# Ticket.overview(
|
||||
# :view => 'some_view_url',
|
||||
# :current_user_id => 123,
|
||||
|
@ -142,7 +141,7 @@ class Ticket < ActiveRecord::Base
|
|||
overview.condition[item] = 'current_user.id'
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
# remember selected view
|
||||
if data[:view] && data[:view] == overview.meta[:url]
|
||||
overview_selected = overview
|
||||
|
@ -192,10 +191,10 @@ class Ticket < ActiveRecord::Base
|
|||
|
||||
# get count
|
||||
count = Ticket.where( :group_id => group_ids ).where( overview.condition ).count()
|
||||
|
||||
|
||||
# get meta info
|
||||
all = overview.meta
|
||||
|
||||
|
||||
# push to result data
|
||||
result.push all.merge( { :count => count } )
|
||||
}
|
||||
|
@ -261,7 +260,7 @@ class Ticket < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
def destroy_dependencies
|
||||
|
||||
|
||||
# delete history
|
||||
History.history_destroy( 'Ticket', self.id )
|
||||
|
||||
|
@ -293,14 +292,14 @@ class Ticket < ActiveRecord::Base
|
|||
belongs_to :ticket_article_type, :class_name => 'Ticket::Article::Type'
|
||||
belongs_to :ticket_article_sender, :class_name => 'Ticket::Article::Sender'
|
||||
belongs_to :created_by, :class_name => 'User'
|
||||
|
||||
|
||||
private
|
||||
def fillup
|
||||
|
||||
|
||||
# if sender is customer, do not change anything
|
||||
sender = Ticket::Article::Sender.where( :id => self.ticket_article_sender_id ).first
|
||||
return if sender == nil || sender['name'] == 'Customer'
|
||||
|
||||
|
||||
type = Ticket::Article::Type.where( :id => self.ticket_article_type_id ).first
|
||||
ticket = Ticket.find(self.ticket_id)
|
||||
|
||||
|
@ -337,10 +336,10 @@ class Ticket < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
def attachment_check
|
||||
|
||||
|
||||
# do nothing if no attachment exists
|
||||
return 1 if self['attachments'] == nil
|
||||
|
||||
|
||||
# store attachments
|
||||
article_store = []
|
||||
self.attachments.each do |attachment|
|
||||
|
@ -363,9 +362,9 @@ class Ticket < ActiveRecord::Base
|
|||
|
||||
type = Ticket::Article::Type.where( :id => self.ticket_article_type_id ).first
|
||||
ticket = Ticket.find(self.ticket_id)
|
||||
|
||||
|
||||
# if sender is agent or system
|
||||
|
||||
|
||||
# create tweet
|
||||
if type['name'] == 'twitter direct-message' || type['name'] == 'twitter status'
|
||||
a = Channel::Twitter2.new
|
||||
|
@ -381,7 +380,7 @@ class Ticket < ActiveRecord::Base
|
|||
self.message_id = message.id
|
||||
self.save
|
||||
end
|
||||
|
||||
|
||||
# post facebook comment
|
||||
if type['name'] == 'facebook'
|
||||
a = Channel::Facebook.new
|
||||
|
@ -393,13 +392,13 @@ class Ticket < ActiveRecord::Base
|
|||
}
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
# send email
|
||||
if type['name'] == 'email'
|
||||
|
||||
|
||||
# build subject
|
||||
subject = ticket.subject_build(self.subject)
|
||||
|
||||
|
||||
# send email
|
||||
a = Channel::IMAP.new
|
||||
message = a.send(
|
||||
|
@ -414,7 +413,7 @@ class Ticket < ActiveRecord::Base
|
|||
:attachments => self.attachments
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# store mail plain
|
||||
Store.add(
|
||||
:object => 'Ticket::Article::Mail',
|
||||
|
@ -428,12 +427,12 @@ class Ticket < ActiveRecord::Base
|
|||
|
||||
class Flag < ActiveRecord::Base
|
||||
end
|
||||
|
||||
|
||||
class Sender < ActiveRecord::Base
|
||||
end
|
||||
|
||||
|
||||
class Type < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -142,14 +142,14 @@ Your #{config.product_name} Team
|
|||
end
|
||||
|
||||
def self.password_reset_via_token(token,password)
|
||||
|
||||
|
||||
# check token
|
||||
token = Token.check( :action => 'PasswordReset', :name => token )
|
||||
return if !token
|
||||
|
||||
|
||||
# reset password
|
||||
token.user.update_attributes( :password => password )
|
||||
|
||||
|
||||
# delete token
|
||||
token.delete
|
||||
token.save
|
||||
|
@ -176,7 +176,7 @@ Your #{config.product_name} Team
|
|||
:username => authorization[:username]
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
# set roles
|
||||
roles = []
|
||||
user.roles.select('id, name').where( :active => true ).each { |role|
|
||||
|
@ -206,6 +206,54 @@ Your #{config.product_name} Team
|
|||
|
||||
return data
|
||||
end
|
||||
|
||||
def self.user_data_full (user_id)
|
||||
|
||||
# get user
|
||||
user = User.find_fulldata(user_id)
|
||||
|
||||
# do not show password
|
||||
user['password'] = ''
|
||||
|
||||
# TEMP: compat. reasons
|
||||
user['preferences'] = {} if user['preferences'] == nil
|
||||
|
||||
items = []
|
||||
if user['preferences'][:tickets_open].to_i > 0
|
||||
item = {
|
||||
:url => '',
|
||||
:name => 'open',
|
||||
:count => user['preferences'][:tickets_open] || 0,
|
||||
:title => 'Open Tickets',
|
||||
:class => 'user-tickets',
|
||||
:data => 'open'
|
||||
}
|
||||
items.push item
|
||||
end
|
||||
if user['preferences'][:tickets_closed].to_i > 0
|
||||
item = {
|
||||
:url => '',
|
||||
:name => 'closed',
|
||||
:count => user['preferences'][:tickets_closed] || 0,
|
||||
:title => 'Closed Tickets',
|
||||
:class => 'user-tickets',
|
||||
:data => 'closed'
|
||||
}
|
||||
items.push item
|
||||
end
|
||||
|
||||
# show linked topics and items
|
||||
if items.count > 0
|
||||
topic = {
|
||||
:title => 'Tickets',
|
||||
:items => items,
|
||||
}
|
||||
user['links'] = []
|
||||
user['links'].push topic
|
||||
end
|
||||
|
||||
return user
|
||||
end
|
||||
|
||||
# update all users geo data
|
||||
def self.geo_update_all
|
||||
|
|
|
@ -13,8 +13,11 @@ require 'google_oauth2_database'
|
|||
# load notification factory (replace all tags)
|
||||
require 'notification_factory'
|
||||
|
||||
# load gmaps lookup
|
||||
# load lib
|
||||
require 'gmaps'
|
||||
require 'rss'
|
||||
|
||||
require 'web_socket'
|
||||
|
||||
# Initialize the rails application
|
||||
Zammad::Application.initialize!
|
||||
|
|
31
lib/rss.rb
Normal file
31
lib/rss.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
module RSS
|
||||
def self.fetch(url, limit = 10)
|
||||
cache_key = 'rss::' + url
|
||||
items = Rails.cache.read( cache_key )
|
||||
if !items
|
||||
puts 'fetch rss...'
|
||||
response = Net::HTTP.get_response( URI.parse(url) )
|
||||
if response.code.to_s != '200'
|
||||
return
|
||||
end
|
||||
rss = SimpleRSS.parse response.body
|
||||
items = []
|
||||
fetched = 0
|
||||
rss.items.each { |item|
|
||||
record = {
|
||||
:id => item.id,
|
||||
:title => item.title,
|
||||
:summary => item.summary,
|
||||
:link => item.link,
|
||||
:published => item.published
|
||||
}
|
||||
items.push record
|
||||
fetched += 1
|
||||
break item if fetched == limit.to_i
|
||||
}
|
||||
Rails.cache.write( cache_key, items, :expires_in => 4.hours )
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
end
|
175
lib/web_socket.rb
Normal file
175
lib/web_socket.rb
Normal file
|
@ -0,0 +1,175 @@
|
|||
require 'json'
|
||||
|
||||
module Session
|
||||
@path = '/tmp/websocket'
|
||||
|
||||
def self.create( client_id, session )
|
||||
path = @path + '/' + client_id.to_s
|
||||
FileUtils.mkpath path
|
||||
File.open( path + '/session', 'w' ) { |file|
|
||||
user = { :id => session['id'] }
|
||||
file.puts Marshal.dump(user)
|
||||
}
|
||||
end
|
||||
|
||||
def self.get( client_id )
|
||||
session_file = @path + '/' + client_id.to_s + '/session'
|
||||
data = nil
|
||||
return if !File.exist? session_file
|
||||
File.open( session_file, 'r' ) { |file|
|
||||
all = ''
|
||||
while line = file.gets
|
||||
all = all + line
|
||||
end
|
||||
begin
|
||||
data = Marshal.load( all )
|
||||
rescue
|
||||
return
|
||||
end
|
||||
}
|
||||
return data
|
||||
end
|
||||
|
||||
def self.transaction( client_id, data )
|
||||
filename = @path + '/' + client_id.to_s + '/transaction-' + Time.new().to_i.to_s
|
||||
if File::exists?( filename )
|
||||
filename = @path + '/' + client_id.to_s + '/transaction-' + Time.new().to_i.to_s + '-1'
|
||||
if File::exists?( filename )
|
||||
filename = @path + '/' + client_id.to_s + '/transaction-' + Time.new().to_i.to_s + '-2'
|
||||
if File::exists?( filename )
|
||||
filename = @path + '/' + client_id.to_s + '/transaction-' + Time.new().to_i.to_s + '-3'
|
||||
if File::exists?( filename )
|
||||
filename = @path + '/' + client_id.to_s + '/transaction-' + Time.new().to_i.to_s + '-4'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
File.open( filename, 'w' ) { |file|
|
||||
file.puts data.to_json
|
||||
}
|
||||
return true
|
||||
end
|
||||
|
||||
def self.jobs
|
||||
state_client_ids = {}
|
||||
while true
|
||||
client_ids = self.sessions
|
||||
client_ids.each { |client_id|
|
||||
|
||||
if !state_client_ids[client_id]
|
||||
state_client_ids[client_id] = {}
|
||||
end
|
||||
|
||||
# get current user
|
||||
user_session = Session.get( client_id )
|
||||
next if !user_session
|
||||
next if !user_session[:id]
|
||||
user = User.find( user_session[:id] )
|
||||
|
||||
# overviews
|
||||
result = Ticket.overview(
|
||||
:current_user_id => user.id,
|
||||
)
|
||||
if state_client_ids[client_id][:overview] != result
|
||||
state_client_ids[client_id][:overview] = result
|
||||
|
||||
# send update to browser
|
||||
Session.transaction( client_id, {
|
||||
:action => 'load',
|
||||
:data => result,
|
||||
:event => 'navupdate_ticket_overview',
|
||||
})
|
||||
end
|
||||
|
||||
# recent viewed
|
||||
recent_viewed = History.recent_viewed(user)
|
||||
if state_client_ids[client_id][:recent_viewed] != recent_viewed
|
||||
state_client_ids[client_id][:recent_viewed] = recent_viewed
|
||||
|
||||
# tickets and users
|
||||
recent_viewed = History.recent_viewed_fulldata(user)
|
||||
|
||||
# send update to browser
|
||||
Session.transaction( client_id, {
|
||||
:action => 'load',
|
||||
:data => recent_viewed,
|
||||
:event => 'update_recent_viewed',
|
||||
})
|
||||
end
|
||||
|
||||
# activity stream
|
||||
activity_stream = History.activity_stream(user)
|
||||
if state_client_ids[client_id][:activity_stream] != activity_stream
|
||||
state_client_ids[client_id][:activity_stream] = activity_stream
|
||||
|
||||
activity_stream = History.activity_stream_fulldata(user)
|
||||
|
||||
# send update to browser
|
||||
Session.transaction( client_id, {
|
||||
:event => 'activity_stream_rebuild',
|
||||
:collection => 'activity_stream',
|
||||
:data => activity_stream,
|
||||
})
|
||||
end
|
||||
|
||||
# rss view
|
||||
rss_items = RSS.fetch( 'http://www.heise.de/newsticker/heise-atom.xml', 8 )
|
||||
if state_client_ids[client_id][:rss_items] != rss_items
|
||||
state_client_ids[client_id][:rss_items] = rss_items
|
||||
|
||||
# send update to browser
|
||||
Session.transaction( client_id, {
|
||||
:event => 'rss_rebuild',
|
||||
:collection => 'dashboard_rss',
|
||||
:data => {
|
||||
head: 'Heise ATOM',
|
||||
items: rss_items,
|
||||
},
|
||||
})
|
||||
end
|
||||
sleep 1
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def self.sessions
|
||||
path = @path + '/'
|
||||
data = []
|
||||
Dir.foreach( path ) do |entry|
|
||||
if entry != '.' && entry != '..'
|
||||
data.push entry
|
||||
end
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
def self.queue( client_id )
|
||||
path = @path + '/' + client_id.to_s + '/'
|
||||
data = []
|
||||
Dir.foreach( path ) do |entry|
|
||||
if /^transaction/.match( entry )
|
||||
data.push Session.queue_file( path + entry )
|
||||
end
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
def self.queue_file( filename )
|
||||
data = nil
|
||||
File.open( filename, 'r' ) { |file|
|
||||
all = ''
|
||||
while line = file.gets
|
||||
all = all + line
|
||||
end
|
||||
data = JSON.parse( all )
|
||||
}
|
||||
File.delete( filename )
|
||||
return data
|
||||
end
|
||||
|
||||
def self.destory( client_id )
|
||||
path = @path + '/' + client_id.to_s
|
||||
FileUtils.rm_rf path
|
||||
end
|
||||
|
||||
end
|
86
script/websocket-server.rb
Normal file
86
script/websocket-server.rb
Normal file
|
@ -0,0 +1,86 @@
|
|||
$LOAD_PATH << './lib'
|
||||
require 'rubygems'
|
||||
require 'eventmachine'
|
||||
require 'em-websocket'
|
||||
require 'json'
|
||||
require 'fileutils'
|
||||
require 'web_socket'
|
||||
require 'optparse'
|
||||
|
||||
# Look for -o with argument, and -I and -D boolean arguments
|
||||
options = {
|
||||
:p => 6042,
|
||||
:b => '0.0.0.0',
|
||||
}
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Usage: websocket-server.rb [options]"
|
||||
|
||||
opts.on("-p", "--port [OPT]", "port of websocket server") do |p|
|
||||
options[:p] = p
|
||||
end
|
||||
opts.on("-b", "--bind [OPT]", "bind address") do |b|
|
||||
options[:b] = b
|
||||
end
|
||||
end.parse!
|
||||
|
||||
puts "Starting websocket server on #{ options[:b] }:#{ options[:p] }"
|
||||
|
||||
@clients = {}
|
||||
EventMachine.run {
|
||||
EventMachine::WebSocket.start( :host => options[:b], :port => options[:p] ) do |ws|
|
||||
|
||||
# register client connection
|
||||
ws.onopen {
|
||||
client_id = ws.object_id
|
||||
puts 'Client ' + client_id.to_s + ' connected'
|
||||
|
||||
if !@clients.include? client_id
|
||||
@clients[client_id] = {
|
||||
:websocket => ws,
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
# unregister client connection
|
||||
ws.onclose {
|
||||
client_id = ws.object_id
|
||||
puts 'Client ' + client_id.to_s + ' disconnected'
|
||||
|
||||
if @clients.include? client_id
|
||||
@clients.delete client_id
|
||||
end
|
||||
Session.destory( client_id )
|
||||
}
|
||||
|
||||
# manage messages
|
||||
ws.onmessage { |msg|
|
||||
|
||||
client_id = ws.object_id
|
||||
puts 'From Client ' + client_id.to_s + ' received message: ' + msg
|
||||
data = JSON.parse(msg)
|
||||
|
||||
# get session
|
||||
if data['action'] == 'login'
|
||||
@clients[client_id][:session] = data['session']
|
||||
Session.create( client_id, data['session'] )
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
EventMachine.add_periodic_timer(0.4) {
|
||||
# puts "loop"
|
||||
@clients.each { |client_id, client|
|
||||
# puts 'checking client...' + client_id.to_s
|
||||
begin
|
||||
queue = Session.queue( client_id )
|
||||
if queue && queue[0]
|
||||
puts "send to #{client_id} " + queue.inspect
|
||||
client[:websocket].send( queue.to_json )
|
||||
end
|
||||
rescue
|
||||
puts 'problem'
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue