sessions.rb 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. require 'json'
  2. require 'rss'
  3. require 'session_helper'
  4. module Sessions
  5. # get application root directory
  6. @root = Dir.pwd.to_s
  7. if !@root || @root.empty? || @root == '/'
  8. @root = Rails.root
  9. end
  10. # get working directories
  11. @path = @root + '/tmp/websocket'
  12. @pid = @root + '/tmp/pids/sessionworker.pid'
  13. # create global vars for threads
  14. @@user_threads = {}
  15. @@client_threads = {}
  16. def self.create( client_id, session, meta )
  17. path = @path + '/' + client_id.to_s
  18. FileUtils.mkpath path
  19. meta[:last_ping] = Time.new.to_i.to_s
  20. File.open( path + '/session', 'wb' ) { |file|
  21. data = {
  22. :user => {
  23. :id => session['id'],
  24. },
  25. :meta => meta,
  26. }
  27. # puts 'CREATE' + Marshal.dump(data)
  28. file.write Marshal.dump(data)
  29. }
  30. # send update to browser
  31. if session['id']
  32. self.send( client_id, {
  33. :event => 'ws:login',
  34. :data => { :success => true },
  35. })
  36. end
  37. end
  38. def self.spool_create( msg )
  39. path = @path + '/spool/'
  40. FileUtils.mkpath path
  41. file = Time.new.to_f.to_s + '-' + rand(99999).to_s
  42. File.open( path + '/' + file , 'wb' ) { |file|
  43. data = {
  44. :msg => msg,
  45. :timestamp => Time.now.to_i,
  46. }
  47. # puts 'CREATE' + Marshal.dump(data)
  48. file.write data.to_json
  49. }
  50. end
  51. def self.spool_list( timestamp, current_user_id )
  52. path = @path + '/spool/'
  53. FileUtils.mkpath path
  54. data = []
  55. to_delete = []
  56. files = []
  57. Dir.foreach( path ) {|entry|
  58. next if entry == '.' || entry == '..'
  59. files.push entry
  60. }
  61. files.sort.each {|entry|
  62. filename = path + '/' + entry
  63. next if !File::exists?( filename )
  64. File.open( filename, 'rb' ) { |file|
  65. all = file.read
  66. spool = JSON.parse( all )
  67. begin
  68. message_parsed = JSON.parse( spool['msg'] )
  69. rescue => e
  70. log 'error', "can't parse spool message: #{ message }, #{ e.inspect }"
  71. next
  72. end
  73. # ignore message older then 48h
  74. if spool['timestamp'] + (2 * 86400) < Time.now.to_i
  75. to_delete.push path + '/' + entry
  76. next
  77. end
  78. # add spool attribute to push spool info to clients
  79. message_parsed['spool'] = true
  80. # only send not already now messages
  81. if !timestamp || timestamp < spool['timestamp']
  82. # spool to recipient list
  83. if message_parsed['recipient'] && message_parsed['recipient']['user_id']
  84. message_parsed['recipient']['user_id'].each { |user_id|
  85. if current_user_id == user_id
  86. item = {
  87. :type => 'direct',
  88. :message => message_parsed,
  89. }
  90. data.push item
  91. end
  92. }
  93. # spool to every client
  94. else
  95. item = {
  96. :type => 'broadcast',
  97. :message => message_parsed,
  98. }
  99. data.push item
  100. end
  101. end
  102. }
  103. }
  104. to_delete.each {|file|
  105. File.delete(file)
  106. }
  107. return data
  108. end
  109. def self.list
  110. client_ids = self.sessions
  111. session_list = {}
  112. client_ids.each { |client_id|
  113. data = self.get(client_id)
  114. next if !data
  115. session_list[client_id] = data
  116. }
  117. return session_list
  118. end
  119. def self.touch( client_id )
  120. data = self.get(client_id)
  121. return if !data
  122. path = @path + '/' + client_id.to_s
  123. data[:meta][:last_ping] = Time.new.to_i.to_s
  124. File.open( path + '/session', 'wb' ) { |file|
  125. file.write Marshal.dump(data)
  126. }
  127. return true
  128. end
  129. def self.get( client_id )
  130. session_file = @path + '/' + client_id.to_s + '/session'
  131. data = nil
  132. return if !File.exist? session_file
  133. begin
  134. File.open( session_file, 'rb' ) { |file|
  135. file.flock( File::LOCK_EX )
  136. all = file.read
  137. file.flock( File::LOCK_UN )
  138. data = Marshal.load( all )
  139. }
  140. rescue Exception => e
  141. File.delete(session_file)
  142. puts "Error reading '#{session_file}':"
  143. puts e.inspect
  144. return
  145. end
  146. return data
  147. end
  148. def self.send( client_id, data )
  149. path = @path + '/' + client_id.to_s + '/'
  150. filename = 'send-' + Time.new().to_f.to_s# + '-' + rand(99999999).to_s
  151. check = true
  152. count = 0
  153. while check
  154. if File::exists?( path + filename )
  155. count += 1
  156. filename = filename + '-' + count
  157. # filename = filename + '-' + rand(99999).to_s
  158. # filename = filename + '-' + rand(99999).to_s
  159. else
  160. check = false
  161. end
  162. end
  163. return false if !File.directory? path
  164. File.open( path + 'a-' + filename, 'wb' ) { |file|
  165. file.flock( File::LOCK_EX )
  166. file.write data.to_json
  167. file.flock( File::LOCK_UN )
  168. file.close
  169. }
  170. return false if !File.exists?( path + 'a-' + filename )
  171. FileUtils.mv( path + 'a-' + filename, path + filename )
  172. return true
  173. end
  174. def self.jobs
  175. # just make sure that spool path exists
  176. if !File::exists?( @path )
  177. FileUtils.mkpath @path
  178. end
  179. Thread.abort_on_exception = true
  180. while true
  181. client_ids = self.sessions
  182. client_ids.each { |client_id|
  183. # connection already open
  184. next if @@client_threads[client_id]
  185. # get current user
  186. session_data = Sessions.get( client_id )
  187. next if !session_data
  188. next if !session_data[:user]
  189. next if !session_data[:user][:id]
  190. user = User.find( session_data[:user][:id] )
  191. next if !user
  192. # start user thread
  193. start_user_thread = false
  194. if !@@user_threads[user.id]
  195. start_user_thread = true
  196. @@user_threads[user.id] = Thread.new {
  197. UserState.new(user.id)
  198. @@user_threads[user.id] = nil
  199. puts "close user(#{user.id}) thread"
  200. # raise "Exception from thread"
  201. }
  202. end
  203. # wait with client thread unil user thread has done some little work
  204. if start_user_thread
  205. sleep 0.5
  206. end
  207. # start client thread
  208. if !@@client_threads[client_id]
  209. @@client_threads[client_id] = Thread.new {
  210. ClientState.new(client_id)
  211. @@client_threads[client_id] = nil
  212. puts "close client(#{client_id}) thread"
  213. # raise "Exception from thread"
  214. }
  215. end
  216. }
  217. # system settings
  218. sleep 0.5
  219. end
  220. end
  221. def self.sessions
  222. path = @path + '/'
  223. # just make sure that spool path exists
  224. if !File::exists?( path )
  225. FileUtils.mkpath path
  226. end
  227. data = []
  228. Dir.foreach( path ) do |entry|
  229. next if entry == '.' || entry == '..' || entry == 'spool'
  230. data.push entry.to_s
  231. end
  232. return data
  233. end
  234. def self.queue( client_id )
  235. path = @path + '/' + client_id.to_s + '/'
  236. data = []
  237. files = []
  238. Dir.foreach( path ) {|entry|
  239. next if entry == '.' || entry == '..'
  240. files.push entry
  241. }
  242. files.sort.each {|entry|
  243. filename = path + '/' + entry
  244. if /^send/.match( entry )
  245. data.push Sessions.queue_file( path, entry )
  246. end
  247. }
  248. return data
  249. end
  250. def self.queue_file( path, filename )
  251. file_old = path + filename
  252. file_new = path + 'a-' + filename
  253. FileUtils.mv( file_old, file_new )
  254. data = nil
  255. all = ''
  256. File.open( file_new, 'rb' ) { |file|
  257. all = file.read
  258. }
  259. File.delete( file_new )
  260. data = JSON.parse( all )
  261. return data
  262. end
  263. def self.broadcast( data )
  264. # list all current clients
  265. client_list = self.list
  266. client_list.each {|local_client_id, local_client|
  267. Sessions.send( local_client_id, data )
  268. }
  269. return true
  270. end
  271. def self.destory( client_id )
  272. path = @path + '/' + client_id.to_s
  273. FileUtils.rm_rf path
  274. end
  275. end
  276. module CacheIn
  277. @@data = {}
  278. @@data_time = {}
  279. @@expires_in = {}
  280. @@expires_in_ttl = {}
  281. def self.set( key, value, params = {} )
  282. # puts 'CacheIn.set:' + key + '-' + value.inspect
  283. if params[:expires_in]
  284. @@expires_in[key] = Time.now + params[:expires_in]
  285. @@expires_in_ttl[key] = params[:expires_in]
  286. end
  287. @@data[ key ] = value
  288. @@data_time[ key ] = Time.now
  289. end
  290. def self.expired( key, params = {} )
  291. # expire if value never was set
  292. return true if !@@data.include? key
  293. # ignore_expire
  294. return false if params[:ignore_expire]
  295. # set re_expire
  296. if params[:re_expire]
  297. if @@expires_in[key]
  298. @@expires_in[key] = Time.now + @@expires_in_ttl[key]
  299. end
  300. return false
  301. end
  302. # check if expired
  303. if @@expires_in[key]
  304. return true if @@expires_in[key] < Time.now
  305. return false
  306. end
  307. # return false if key was set without expires_in
  308. return false
  309. end
  310. def self.get_time( key, params = {} )
  311. data = self.get( key, params )
  312. if data
  313. return @@data_time[key]
  314. end
  315. return nil
  316. end
  317. def self.get( key, params = {} )
  318. # puts 'CacheIn.get:' + key + '-' + @@data[ key ].inspect
  319. return if self.expired( key, params )
  320. @@data[ key ]
  321. end
  322. end
  323. class UserState
  324. def initialize( user_id )
  325. @user_id = user_id
  326. @data = {}
  327. @cache_key = 'user_' + user_id.to_s
  328. self.log 'notify', "---user started user state"
  329. CacheIn.set( 'last_run_' + user_id.to_s , true, { :expires_in => 20.seconds } )
  330. self.fetch
  331. end
  332. def fetch
  333. user = User.find( @user_id )
  334. return if !user
  335. while true
  336. # check if user is still with min one open connection
  337. if !CacheIn.get( 'last_run_' + user.id.to_s )
  338. self.log 'notify', "---user - closeing thread - no open user connection"
  339. return
  340. end
  341. self.log 'notice', "---user - fetch user data"
  342. # overview
  343. cache_key = @cache_key + '_overview'
  344. if CacheIn.expired(cache_key)
  345. overview = Ticket::Overviews.list(
  346. :current_user => user,
  347. )
  348. overview_cache = CacheIn.get( cache_key, { :re_expire => true } )
  349. self.log 'notice', 'fetch overview - ' + cache_key
  350. if overview != overview_cache
  351. self.log 'notify', 'fetch overview changed - ' + cache_key
  352. # puts overview.inspect
  353. # puts '------'
  354. # puts overview_cache.inspect
  355. CacheIn.set( cache_key, overview, { :expires_in => 4.seconds } )
  356. end
  357. end
  358. # overview lists
  359. overviews = Ticket::Overviews.all(
  360. :current_user => user,
  361. )
  362. overviews.each { |overview|
  363. cache_key = @cache_key + '_overview_data_' + overview.link
  364. if CacheIn.expired(cache_key)
  365. overview_data = Ticket::Overviews.list(
  366. :view => overview.link,
  367. :current_user => user,
  368. :array => true,
  369. )
  370. overview_data_cache = CacheIn.get( cache_key, { :re_expire => true } )
  371. self.log 'notice', 'fetch overview_data - ' + cache_key
  372. if overview_data != overview_data_cache
  373. self.log 'notify', 'fetch overview_data changed - ' + cache_key
  374. CacheIn.set( cache_key, overview_data, { :expires_in => 5.seconds } )
  375. end
  376. end
  377. }
  378. # create_attributes
  379. cache_key = @cache_key + '_ticket_create_attributes'
  380. if CacheIn.expired(cache_key)
  381. ticket_create_attributes = Ticket::ScreenOptions.attributes_to_change(
  382. :current_user_id => user.id,
  383. )
  384. ticket_create_attributes_cache = CacheIn.get( cache_key, { :re_expire => true } )
  385. self.log 'notice', 'fetch ticket_create_attributes - ' + cache_key
  386. if ticket_create_attributes != ticket_create_attributes_cache
  387. self.log 'notify', 'fetch ticket_create_attributes changed - ' + cache_key
  388. CacheIn.set( cache_key, ticket_create_attributes, { :expires_in => 2.minutes } )
  389. end
  390. end
  391. # recent viewed
  392. cache_key = @cache_key + '_recent_viewed'
  393. if CacheIn.expired(cache_key)
  394. recent_viewed = RecentView.list_fulldata( user, 10 )
  395. recent_viewed_cache = CacheIn.get( cache_key, { :re_expire => true } )
  396. self.log 'notice', 'fetch recent_viewed - ' + cache_key
  397. if recent_viewed != recent_viewed_cache
  398. self.log 'notify', 'fetch recent_viewed changed - ' + cache_key
  399. recent_viewed_full = RecentView.list_fulldata( user, 10 )
  400. CacheIn.set( cache_key, recent_viewed, { :expires_in => 5.seconds } )
  401. CacheIn.set( cache_key + '_push', recent_viewed_full )
  402. end
  403. end
  404. # activity steam
  405. cache_key = @cache_key + '_activity_stream'
  406. if CacheIn.expired(cache_key)
  407. activity_stream = History.activity_stream( user )
  408. activity_stream_cache = CacheIn.get( cache_key, { :re_expire => true } )
  409. self.log 'notice', 'fetch activity_stream - ' + cache_key
  410. if activity_stream != activity_stream_cache
  411. self.log 'notify', 'fetch activity_stream changed - ' + cache_key
  412. activity_stream_full = History.activity_stream_fulldata( user )
  413. CacheIn.set( cache_key, activity_stream, { :expires_in => 0.75.minutes } )
  414. CacheIn.set( cache_key + '_push', activity_stream_full )
  415. end
  416. end
  417. # rss
  418. cache_key = @cache_key + '_rss'
  419. if CacheIn.expired(cache_key)
  420. url = 'http://www.heise.de/newsticker/heise-atom.xml'
  421. rss_items = Rss.fetch( url, 8 )
  422. rss_items_cache = CacheIn.get( cache_key, { :re_expire => true } )
  423. self.log 'notice', 'fetch rss - ' + cache_key
  424. if rss_items != rss_items_cache
  425. self.log 'notify', 'fetch rss changed - ' + cache_key
  426. CacheIn.set( cache_key, rss_items, { :expires_in => 2.minutes } )
  427. CacheIn.set( cache_key + '_push', {
  428. head: 'Heise ATOM',
  429. items: rss_items,
  430. })
  431. end
  432. end
  433. # auto population of default collections
  434. self.log 'notice', "---user - fetch push_collection data"
  435. # get available collections
  436. cache_key = @cache_key + '_push_collections'
  437. collections = CacheIn.get( cache_key )
  438. if !collections
  439. collections = {}
  440. push_collection = SessionHelper::push_collections(user)
  441. push_collection.each { | key, value |
  442. collections[ key ] = true
  443. }
  444. CacheIn.set( cache_key, collections, { :expires_in => 2.minutes } )
  445. end
  446. # check all collections to push
  447. push_collection = {}
  448. collections.each { | key, v |
  449. cache_key = @cache_key + '_push_collections_' + key
  450. if CacheIn.expired(cache_key)
  451. if push_collection.empty?
  452. push_collection = SessionHelper::push_collections(user)
  453. end
  454. push_collection_cache = CacheIn.get( cache_key, { :re_expire => true } )
  455. self.log 'notice', "---user - fetch push_collection data " + cache_key
  456. if !push_collection[key] || !push_collection_cache || push_collection[key] != push_collection_cache || !push_collection[ key ].zip( push_collection_cache ).all? { |x, y| x.attributes == y.attributes }
  457. self.log 'notify', 'fetch push_collection changed - ' + cache_key
  458. CacheIn.set( cache_key, push_collection[key], { :expires_in => 1.minutes } )
  459. end
  460. end
  461. }
  462. self.log 'notice', "---/user-"
  463. sleep 1
  464. end
  465. end
  466. def log( level, data )
  467. return if level == 'notice'
  468. puts "#{Time.now}:user_id(#{ @user_id }) #{ data }"
  469. end
  470. end
  471. class ClientState
  472. def initialize( client_id )
  473. @client_id = client_id
  474. @cache_key = ''
  475. @data = {}
  476. @pushed = {}
  477. self.log 'notify', "---client start ws connection---"
  478. self.fetch
  479. self.log 'notify', "---client exiting ws connection---"
  480. end
  481. def fetch
  482. loop_count = 0
  483. while true
  484. # get connection user
  485. session_data = Sessions.get( @client_id )
  486. return if !session_data
  487. return if !session_data[:user]
  488. return if !session_data[:user][:id]
  489. user = User.lookup( :id => session_data[:user][:id] )
  490. return if !user
  491. # set cache key
  492. @cache_key = 'user_' + user.id.to_s
  493. loop_count += 1
  494. self.log 'notice', "---client - looking for data of user #{user.id}"
  495. # remember last run
  496. CacheIn.set( 'last_run_' + user.id.to_s , true, { :expires_in => 20.seconds } )
  497. # verify already pushed data
  498. if !CacheIn.get( 'pushed_users' + @client_id.to_s )
  499. CacheIn.set( 'pushed_users' + @client_id.to_s , true, { :expires_in => 20.seconds } )
  500. if @pushed[:users]
  501. users = {}
  502. @pushed[:users].each {|user_id, user_o|
  503. self.user( user_id, users )
  504. }
  505. if !users.empty?
  506. users.each {|user_id, user_data|
  507. self.log 'notify', "push update of already pushed user id #{user_id}"
  508. }
  509. # send update to browser
  510. self.send({
  511. :data => {
  512. :collections => {
  513. :User => users,
  514. },
  515. },
  516. :event => [ 'loadCollection', 'ticket_overview_rebuild' ],
  517. });
  518. end
  519. end
  520. end
  521. # verify already pushed data
  522. if !CacheIn.get( 'pushed_tickets' + @client_id.to_s )
  523. CacheIn.set( 'pushed_tickets' + @client_id.to_s , true, { :expires_in => 20.seconds } )
  524. if @pushed[:tickets]
  525. tickets = {}
  526. users = {}
  527. @pushed[:tickets].each {|ticket_id, ticket_data|
  528. self.ticket( ticket_id, tickets, users )
  529. }
  530. if !tickets.empty?
  531. tickets.each {|id, ticket|
  532. self.log 'notify', "push update of already pushed ticket id #{id}"
  533. }
  534. # send update to browser
  535. self.send({
  536. :data => {
  537. :collections => {
  538. :Ticket => tickets,
  539. :User => users,
  540. },
  541. },
  542. :event => [ 'loadCollection', 'ticket_overview_rebuild' ],
  543. });
  544. end
  545. end
  546. end
  547. # overview
  548. cache_key = @cache_key + '_overview'
  549. overview_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  550. if overview_time && @data[:overview_time] != overview_time
  551. @data[:overview_time] = overview_time
  552. overview = CacheIn.get( cache_key, { :ignore_expire => true } )
  553. self.log 'notify', "push overview for user #{user.id}"
  554. # send update to browser
  555. self.send({
  556. :event => 'navupdate_ticket_overview',
  557. :data => overview,
  558. })
  559. end
  560. # overview_data
  561. overviews = Ticket::Overviews.all(
  562. :current_user => user,
  563. )
  564. overviews.each { |overview|
  565. cache_key = @cache_key + '_overview_data_' + overview.link
  566. overview_data_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  567. if overview_data_time && @data[cache_key] != overview_data_time
  568. @data[cache_key] = overview_data_time
  569. overview_data = CacheIn.get( cache_key, { :ignore_expire => true } )
  570. self.log 'notify', "push overview_data #{overview.link} for user #{user.id}"
  571. users = {}
  572. tickets = {}
  573. overview_data[:ticket_ids].each {|ticket_id|
  574. self.ticket( ticket_id, tickets, users )
  575. }
  576. # get groups
  577. group_ids = []
  578. Group.where( :active => true ).each { |group|
  579. group_ids.push group.id
  580. }
  581. agents = {}
  582. Ticket::ScreenOptions.agents.each { |user|
  583. agents[ user.id ] = 1
  584. }
  585. groups_users = {}
  586. groups_users[''] = []
  587. group_ids.each {|group_id|
  588. groups_users[ group_id ] = []
  589. Group.find(group_id).users.each {|user|
  590. next if !agents[ user.id ]
  591. groups_users[ group_id ].push user.id
  592. if !users[user.id]
  593. users[user.id] = User.user_data_full(user.id)
  594. end
  595. }
  596. }
  597. # send update to browser
  598. self.send({
  599. :data => {
  600. :users => users,
  601. :tickets => tickets,
  602. },
  603. :event => [ 'loadAssets' ]
  604. })
  605. self.send({
  606. :data => {
  607. :overview => overview_data[:overview],
  608. :ticket_ids => overview_data[:ticket_ids],
  609. :tickets_count => overview_data[:tickets_count],
  610. :bulk => {
  611. :group_id__owner_id => groups_users,
  612. :owner_id => [],
  613. },
  614. },
  615. :event => [ 'ticket_overview_rebuild' ],
  616. :collection => 'ticket_overview_' + overview.link.to_s,
  617. })
  618. end
  619. }
  620. # ticket_create_attributes
  621. cache_key = @cache_key + '_ticket_create_attributes'
  622. ticket_create_attributes_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  623. if ticket_create_attributes_time && @data[:ticket_create_attributes_time] != ticket_create_attributes_time
  624. @data[:ticket_create_attributes_time] = ticket_create_attributes_time
  625. create_attributes = CacheIn.get( cache_key, { :ignore_expire => true } )
  626. users = {}
  627. create_attributes[:owner_id].each {|user_id|
  628. if !users[user_id]
  629. users[user_id] = User.user_data_full(user_id)
  630. end
  631. }
  632. data = {
  633. :users => users,
  634. :edit_form => create_attributes,
  635. }
  636. self.log 'notify', "push ticket_create_attributes for user #{user.id}"
  637. # send update to browser
  638. self.send({
  639. :collection => 'ticket_create_attributes',
  640. :data => data,
  641. })
  642. end
  643. # recent viewed
  644. cache_key = @cache_key + '_recent_viewed'
  645. recent_viewed_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  646. if recent_viewed_time && @data[:recent_viewed_time] != recent_viewed_time
  647. @data[:recent_viewed_time] = recent_viewed_time
  648. recent_viewed = CacheIn.get( cache_key, { :ignore_expire => true } )
  649. self.log 'notify', "push recent_viewed for user #{user.id}"
  650. # send update to browser
  651. r = CacheIn.get( cache_key + '_push', { :ignore_expire => true } )
  652. self.send({
  653. :event => 'update_recent_viewed',
  654. :data => r,
  655. })
  656. end
  657. # activity stream
  658. cache_key = @cache_key + '_activity_stream'
  659. activity_stream_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  660. if activity_stream_time && @data[:activity_stream_time] != activity_stream_time
  661. @data[:activity_stream_time] = activity_stream_time
  662. activity_stream = CacheIn.get( cache_key, { :ignore_expire => true } )
  663. self.log 'notify', "push activity_stream for user #{user.id}"
  664. # send update to browser
  665. r = CacheIn.get( cache_key + '_push', { :ignore_expire => true } )
  666. self.send({
  667. :event => 'activity_stream_rebuild',
  668. :collection => 'activity_stream',
  669. :data => r,
  670. })
  671. end
  672. # rss
  673. cache_key = @cache_key + '_rss'
  674. rss_items_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  675. if rss_items_time && @data[:rss_time] != rss_items_time
  676. @data[:rss_time] = rss_items_time
  677. rss_items = CacheIn.get( cache_key, { :ignore_expire => true } )
  678. self.log 'notify', "push rss for user #{user.id}"
  679. # send update to browser
  680. r = CacheIn.get( cache_key + '_push', { :ignore_expire => true } )
  681. self.send({
  682. :event => 'rss_rebuild',
  683. :collection => 'dashboard_rss',
  684. :data => r,
  685. })
  686. end
  687. # push_collections
  688. cache_key = @cache_key + '_push_collections'
  689. collections = CacheIn.get( cache_key ) || {}
  690. collections.each { | key, v |
  691. collection_cache_key = @cache_key + '_push_collections_' + key
  692. collection_time = CacheIn.get_time( collection_cache_key, { :ignore_expire => true } )
  693. if collection_time && @data[ collection_cache_key + '_time' ] != collection_time
  694. @data[ collection_cache_key + '_time' ] = collection_time
  695. push_collections = CacheIn.get( collection_cache_key, { :ignore_expire => true } )
  696. self.log 'notify', "push push_collections #{key} for user #{user.id}"
  697. # send update to browser
  698. data = {}
  699. data['collections'] = {}
  700. data['collections'][key] = push_collections
  701. self.send({
  702. :event => 'resetCollection',
  703. :data => data,
  704. })
  705. end
  706. }
  707. self.log 'notice', "---/client-"
  708. # start faster in the beginnig
  709. if loop_count < 20
  710. sleep 0.6
  711. else
  712. sleep 1
  713. end
  714. end
  715. end
  716. # add ticket if needed
  717. def ticket( ticket_id, tickets, users )
  718. if !@pushed[:tickets]
  719. @pushed[:tickets] = {}
  720. end
  721. ticket = Ticket.lookup( :id => ticket_id )
  722. if @pushed[:tickets][ticket_id] != ticket['updated_at']
  723. @pushed[:tickets][ticket_id] = ticket['updated_at']
  724. tickets[ticket_id] = ticket
  725. end
  726. # add users if needed
  727. self.user( ticket['owner_id'], users )
  728. self.user( ticket['customer_id'], users )
  729. self.user( ticket['created_by_id'], users )
  730. self.user( ticket['updated_by_id'], users )
  731. end
  732. # add user if needed
  733. def user( user_id, users )
  734. if !@pushed[:users]
  735. @pushed[:users] = {}
  736. end
  737. # get user
  738. user = User.user_data_full( user_id )
  739. # user is already on client and not changed
  740. return if @pushed[:users][ user_id ] == user['updated_at']
  741. @pushed[:users][user_id] = user['updated_at']
  742. # user not on client or different
  743. self.log 'notice', 'push user ... ' + user['login']
  744. users[ user_id ] = user
  745. end
  746. # send update to browser
  747. def send( data )
  748. Sessions.send( @client_id, data )
  749. end
  750. def log( level, data )
  751. return if level == 'notice'
  752. puts "#{Time.now}:client(#{ @client_id }) #{ data }"
  753. end
  754. end