diff --git a/app/assets/javascripts/app/controllers/_application_controller.js.coffee b/app/assets/javascripts/app/controllers/_application_controller.js.coffee
index 8bd408bce..17b73780a 100644
--- a/app/assets/javascripts/app/controllers/_application_controller.js.coffee
+++ b/app/assets/javascripts/app/controllers/_application_controller.js.coffee
@@ -387,6 +387,26 @@ class App.Controller extends Spine.Controller
processData: true
)
+ prepareForObjectList: (items) ->
+ for item in items
+
+ item.link = ''
+ item.title = '???'
+
+ # convert backend name space to local name space
+ item.object = item.object.replace("::", '')
+
+ # lookup real data
+ if App[item.object]
+ object = App[item.object].find( item.o_id )
+ item.link = object.uiUrl()
+ item.title = object.displayName()
+ item.object_name = object.objectDisplayName()
+ item.cssIcon = object.iconActivity( @Session.all() )
+
+ item.created_by = App.User.retrieve( item.created_by_id )
+ items
+
ws_send: (data) ->
App.Event.trigger( 'ws:send', JSON.stringify(data) )
diff --git a/app/assets/javascripts/app/controllers/_dashboard/activity_stream.js.coffee b/app/assets/javascripts/app/controllers/_dashboard/activity_stream.js.coffee
index 9f3c12835..931927d0b 100644
--- a/app/assets/javascripts/app/controllers/_dashboard/activity_stream.js.coffee
+++ b/app/assets/javascripts/app/controllers/_dashboard/activity_stream.js.coffee
@@ -38,24 +38,7 @@ class App.DashboardActivityStream extends App.Controller
@render(items)
render: (items) ->
-
- for item in items
-
- item.link = ''
- item.title = '???'
-
- # convert backend name space to local name space
- item.object = item.object.replace("::", '')
-
- # lookup real data
- if App[item.object]
- object = App[item.object].find( item.o_id )
- item.link = object.uiUrl()
- item.title = object.displayName()
- item.object_name = object.objectDisplayName()
- item.cssIcon = object.iconActivity( @Session.all() )
-
- item.created_by = App.User.retrieve( item.created_by_id )
+ items = @prepareForObjectList(items)
html = App.view('dashboard/activity_stream')(
head: 'Activity Stream',
diff --git a/app/assets/javascripts/app/controllers/navigation.js.coffee b/app/assets/javascripts/app/controllers/navigation.js.coffee
index e9c1ded07..fd2758962 100644
--- a/app/assets/javascripts/app/controllers/navigation.js.coffee
+++ b/app/assets/javascripts/app/controllers/navigation.js.coffee
@@ -216,6 +216,10 @@ class App.Navigation extends App.Controller
@delay( searchFunction, 220, 'search' )
)
+ new App.OnlineNotificationWidget(
+ el: @el
+ )
+
@taskbar = new App.TaskbarWidget( el: @el.find('.tasks') )
emptySearch: (event) =>
@@ -318,8 +322,6 @@ class App.Navigation extends App.Controller
App.Store.write( 'update_recent_viewed', data )
- items = data.recent_viewed
-
# load assets
App.Collection.loadAssets( data.assets )
@@ -332,6 +334,8 @@ class App.Navigation extends App.Controller
delete NavBarRight[key]
# add new views
+ items = data.recent_viewed
+ items = @prepareForObjectList(items)
prio = 8000
for item in items
divider = false
@@ -340,21 +344,6 @@ class App.Navigation extends App.Controller
divider = true
navheader = 'Recent Viewed'
- item.link = ''
- item.title = '???'
-
- # convert backend name space to local name space
- item.object = item.object.replace("::", '')
-
- # lookup real data
- if App[item.object]
- object = App[item.object].find( item.o_id )
- item.link = object.uiUrl()
- item.title = object.displayName()
- item.object_name = object.objectDisplayName()
-
- item.created_by = App.User.find( item.created_by_id )
-
prio++
NavBarRight['RecendViewed::' + item.o_id + item.object + '-' + prio ] = {
prio: prio
diff --git a/app/assets/javascripts/app/controllers/organization_zoom.js.coffee b/app/assets/javascripts/app/controllers/organization_zoom.js.coffee
index b06dc9f4f..6303dc42e 100644
--- a/app/assets/javascripts/app/controllers/organization_zoom.js.coffee
+++ b/app/assets/javascripts/app/controllers/organization_zoom.js.coffee
@@ -27,6 +27,7 @@ class App.OrganizationZoom extends App.Controller
'#organization/zoom/' + @organization_id
activate: =>
+ App.OnlineNotification.seen( 'Organization', @organization_id )
@navupdate '#'
changed: =>
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
index 530eb5b6b..2b822c219 100644
--- a/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
@@ -53,6 +53,7 @@ class App.TicketZoom extends App.Controller
'#ticket/zoom/' + @ticket_id
activate: =>
+ App.OnlineNotification.seen( 'Ticket', @ticket_id )
@navupdate '#'
changed: =>
diff --git a/app/assets/javascripts/app/controllers/user_zoom.js.coffee b/app/assets/javascripts/app/controllers/user_zoom.js.coffee
index 2834c5513..5bb26fcdc 100644
--- a/app/assets/javascripts/app/controllers/user_zoom.js.coffee
+++ b/app/assets/javascripts/app/controllers/user_zoom.js.coffee
@@ -27,6 +27,7 @@ class App.UserZoom extends App.Controller
'#user/zoom/' + @user_id
activate: =>
+ App.OnlineNotification.seen( 'User', @user_id )
@navupdate '#'
changed: =>
diff --git a/app/assets/javascripts/app/controllers/widget/online_notification.js.coffee b/app/assets/javascripts/app/controllers/widget/online_notification.js.coffee
new file mode 100644
index 000000000..669e83f57
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/widget/online_notification.js.coffee
@@ -0,0 +1,85 @@
+class App.OnlineNotificationWidget extends App.Controller
+ constructor: ->
+ super
+
+ @bind 'OnlineNotification::changed', =>
+ @fetch()
+
+ # rebuild widget on auth
+ @bind 'auth', (user) =>
+ if !user
+ @el.find('activity-counter').html('')
+ else
+ if !@access()
+ @el.find('activity-counter').html('')
+ return
+ @start()
+
+ if @access()
+ @start()
+
+ @start()
+ @subscribeId = App.OnlineNotification.subscribe( @start )
+
+ release: =>
+ @stop()
+ App.Model.unsubscribe( @subscribeId )
+
+ access: ->
+ return false if _.isEmpty( @Session.all() )
+ return true if @isRole('Agent')
+ return true if @isRole('Admin')
+ return false
+
+ counterUpdate: (count) =>
+ console.log('counter update', count)
+ if !count
+ @el.find('.activity-counter').remove()
+ return
+
+ if @el.find('.logo .activity-counter')[0]
+ @el.find('.logo .activity-counter').html(count)
+ else
+ @el.find('.logo').append('
' + count.toString() + '
')
+
+ stop: =>
+ @counterUpdate(0)
+ @el.find('.logo').popover('destroy')
+
+ start: =>
+ @stop()
+
+ # show popover
+ items = App.OnlineNotification.search(sortBy: 'created_at', order: 'DESC' )
+ counter = 0
+ for item in items
+ if !item.seen
+ counter = counter + 1
+ @counterUpdate(counter)
+
+ items = @prepareForObjectList(items)
+
+ @el.find('.logo').popover(
+ trigger: 'click'
+ container: 'body'
+ html: true
+ delay: { show: 100, hide: 0 }
+ placement: 'right'
+ title: ->
+ App.i18n.translateInline( 'Notifications' ) + " #{counter}"
+ content: ->
+ # insert data
+ App.view('widget/online_notification')(
+ items: items
+ )
+ ).on('shown.bs.popover', =>
+ # show frontend times
+ @frontendTimeUpdate()
+ )
+
+
+ fetch: =>
+ load = (items) =>
+ App.OnlineNotification.refresh(items)
+ @start()
+ App.OnlineNotification.fetchFull(load)
\ No newline at end of file
diff --git a/app/assets/javascripts/app/models/_application_model.js.coffee b/app/assets/javascripts/app/models/_application_model.js.coffee
index ba15e918d..1cf797746 100644
--- a/app/assets/javascripts/app/models/_application_model.js.coffee
+++ b/app/assets/javascripts/app/models/_application_model.js.coffee
@@ -382,6 +382,37 @@ class App.Model extends Spine.Model
if @SUBSCRIPTION_COLLECTION[subscribeId]
delete @SUBSCRIPTION_COLLECTION[subscribeId]
+ ###
+
+ fetch full collection (with assets)
+
+ App.Model.fetchFull( @callback )
+
+ ###
+ @fetchFull: (callback) ->
+ url = "#{@url}/?full=true"
+
+ App.Ajax.request(
+ type: 'GET'
+ url: url
+ processData: true,
+ success: (data, status, xhr) =>
+
+ # full / load assets
+ if data.assets
+ App.Collection.loadAssets( data.assets )
+
+ # find / load object
+ else
+ App[ @className ].refresh( data )
+
+ # execute callbacks
+ callback(data.stream)
+
+ error: (xhr, statusText, error) =>
+ console.log(statusText, error)
+ )
+
@_bindsEmpty: ->
if @SUBSCRIPTION_ITEM
for id, keys of @SUBSCRIPTION_ITEM
diff --git a/app/assets/javascripts/app/models/online_notification.js.coffee b/app/assets/javascripts/app/models/online_notification.js.coffee
new file mode 100644
index 000000000..e30f9a2dc
--- /dev/null
+++ b/app/assets/javascripts/app/models/online_notification.js.coffee
@@ -0,0 +1,18 @@
+class App.OnlineNotification extends App.Model
+ @configure 'OnlineNotification', 'name', 'seen'
+ @extend Spine.Model.Ajax
+ @url: @apiPath + '/online_notifications'
+
+ ###
+
+ App.OnlineNotification.seen( 'Ticket', 123 )
+
+ ###
+
+ @seen: (object, o_id) ->
+ notifications = App.OnlineNotification.all()
+ for notification in notifications
+ if notification.object is object && notification.o_id.toString() is o_id.toString()
+ if notification.seen isnt true
+ notification.seen = true
+ notification.save()
\ No newline at end of file
diff --git a/app/assets/javascripts/app/views/navigation.jst.eco b/app/assets/javascripts/app/views/navigation.jst.eco
index 47ec01b53..32bb26f56 100644
--- a/app/assets/javascripts/app/views/navigation.jst.eco
+++ b/app/assets/javascripts/app/views/navigation.jst.eco
@@ -5,9 +5,7 @@
-
+
diff --git a/app/assets/javascripts/app/views/widget/online_notification.jst.eco b/app/assets/javascripts/app/views/widget/online_notification.jst.eco
new file mode 100644
index 000000000..3b9791b07
--- /dev/null
+++ b/app/assets/javascripts/app/views/widget/online_notification.jst.eco
@@ -0,0 +1,15 @@
+<% for item in @items: %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/controllers/online_notifications_controller.rb b/app/controllers/online_notifications_controller.rb
new file mode 100644
index 000000000..20a60efb1
--- /dev/null
+++ b/app/controllers/online_notifications_controller.rb
@@ -0,0 +1,91 @@
+# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
+
+class OnlineNotificationsController < ApplicationController
+ before_filter :authentication_check
+
+=begin
+
+Format:
+JSON
+
+Example:
+{
+ "id":1,
+ "name":"some template",
+ "user_id": null,
+ "options":{"a":1,"b":2},
+ "updated_at":"2012-09-14T17:51:53Z",
+ "created_at":"2012-09-14T17:51:53Z",
+ "updated_by_id":2.
+ "created_by_id":2,
+}
+
+=end
+
+=begin
+
+Resource:
+GET /api/v1/templates.json
+
+Response:
+[
+ {
+ "id": 1,
+ "name": "some_name1",
+ ...
+ },
+ {
+ "id": 2,
+ "name": "some_name2",
+ ...
+ }
+]
+
+Test:
+curl http://localhost/api/v1/online_notifications.json -v -u #{login}:#{password}
+
+=end
+
+ def index
+ if params[:full]
+ render :json => OnlineNotification.list_full(current_user, 50)
+ return
+ end
+
+ notifications = OnlineNotification.list(current_user, 50)
+ model_index_render_result(notifications)
+ end
+
+=begin
+
+Resource:
+PUT /api/v1/online_notifications/{id}
+
+Payload:
+{
+ "name": "some name",
+ "options":{"a":1,"b":2},
+}
+
+Response:
+{
+ "id": 1,
+ "name": "some_name",
+ ...
+}
+
+Test:
+curl http://localhost/api/v1/online_notifications -v -u #{login}:#{password} -H "Content-Type: application/json" -X PUT -d '{"name": "some_name","active": true, "note": "some note"}'
+
+=end
+
+ def update
+ notification = OnlineNotification.find(params[:id])
+ if notification.user_id != current_user.id
+ response_access_deny
+ return
+ end
+ model_update_render(OnlineNotification, params)
+ end
+
+end
diff --git a/app/models/observer/ticket/notification.rb b/app/models/observer/ticket/notification.rb
index f80218257..b9ed322b9 100644
--- a/app/models/observer/ticket/notification.rb
+++ b/app/models/observer/ticket/notification.rb
@@ -65,7 +65,8 @@ class Observer::Ticket::Notification < ActiveRecord::Observer
'
},
ticket,
- article
+ article,
+ 'new ticket'
)
end
@@ -96,7 +97,8 @@ class Observer::Ticket::Notification < ActiveRecord::Observer
'
},
ticket,
- article
+ article,
+ 'new ticket'
)
end
@@ -131,7 +133,8 @@ class Observer::Ticket::Notification < ActiveRecord::Observer
'
},
ticket,
- article
+ article,
+ 'follow up'
)
end
@@ -160,14 +163,15 @@ class Observer::Ticket::Notification < ActiveRecord::Observer
'
},
ticket,
- article
+ article,
+ 'follow up',
)
end
end
}
end
- def self.send_notify(data, ticket, article)
+ def self.send_notify(data, ticket, article, type)
# find recipients
recipients = []
@@ -202,6 +206,16 @@ class Observer::Ticket::Notification < ActiveRecord::Observer
recipient_list = ''
notification_subject = ''
recipients.each do |user|
+
+ OnlineNotification.add(
+ :type => type,
+ :object => 'Ticket',
+ :o_id => ticket.id,
+ :seen => false,
+ :created_by_id => UserInfo.current_user_id || 1,
+ :user_id => user.id,
+ )
+
next if !user.email || user.email == ''
# add recipient_list
diff --git a/app/models/online_notification.rb b/app/models/online_notification.rb
new file mode 100644
index 000000000..10b7610b4
--- /dev/null
+++ b/app/models/online_notification.rb
@@ -0,0 +1,144 @@
+# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
+
+class OnlineNotification < ApplicationModel
+ belongs_to :type_lookup, :class_name => 'TypeLookup'
+ belongs_to :object_lookup, :class_name => 'ObjectLookup'
+
+ after_create :notify_clients_after_change
+ after_update :notify_clients_after_change
+ after_destroy :notify_clients_after_change
+
+=begin
+
+add a new online notification for this user
+
+ OnlineNotification.add(
+ :type => 'Assigned to you',
+ :object => 'Ticket',
+ :o_id => ticket.id,
+ :seen => false,
+ :created_by_id => 1,
+ :user_id => 2,
+ )
+
+=end
+
+ def self.add(data)
+
+ # lookups
+ if data[:type]
+ type_id = TypeLookup.by_name( data[:type] )
+ end
+ if data[:object]
+ object_id = ObjectLookup.by_name( data[:object] )
+ end
+
+ record = {
+ :o_id => data[:o_id],
+ :object_lookup_id => object_id,
+ :type_lookup_id => type_id,
+ :seen => data[:seen],
+ :user_id => data[:user_id],
+ :created_by_id => data[:created_by_id]
+ }
+
+ OnlineNotification.create(record)
+ end
+
+=begin
+
+add a new online notification for this user
+
+ OnlineNotification.add(
+ :type => 'Assigned to you',
+ :object => 'Ticket',
+ :o_id => ticket.id,
+ :seen => 1,
+ :created_by_id => 1,
+ :user_id => 2,
+ )
+
+=end
+
+ def self.seen(data)
+ notification = OnlineNotification.find(data[:id])
+ notification.seen = true
+ notification.save
+ end
+
+=begin
+
+remove whole online notifications of an object
+
+ OnlineNotification.remove( 'Ticket', 123 )
+
+=end
+
+ def self.remove( object_name, o_id )
+ object_id = ObjectLookup.by_name( object_name )
+ OnlineNotification.where(
+ :object_lookup_id => object_id,
+ :o_id => o_id,
+ ).destroy_all
+ end
+
+=begin
+
+return all online notifications of an user
+
+ notifications = OnlineNotification.list( user )
+
+=end
+
+ def self.list(user,limit)
+
+ notifications = OnlineNotification.where(:user_id => user.id).
+ order( 'created_at DESC, id DESC' ).
+ limit( limit )
+ list = []
+ notifications.each do |item|
+ data = item.attributes
+ data['object'] = ObjectLookup.by_id( data['object_lookup_id'] )
+ data['type'] = TypeLookup.by_id( data['type_lookup_id'] )
+ data.delete('object_lookup_id')
+ data.delete('type_lookup_id')
+ list.push data
+ end
+ list
+ end
+
+=begin
+
+return all online notifications of an user with assets
+
+ OnlineNotification.list_full( user )
+
+returns:
+
+ list = {
+ :stream => notifications,
+ :assets => assets
+ }
+
+=end
+
+ def self.list_full(user,limit)
+
+ notifications = OnlineNotification.list(user, limit)
+ assets = ApplicationModel.assets_of_object_list(notifications)
+ return {
+ :stream => notifications,
+ :assets => assets
+ }
+ end
+
+ def notify_clients_after_change
+
+ puts "#{ self.class.name } changed " + self.created_at.to_s
+ Sessions.broadcast(
+ :event => 'OnlineNotification::changed',
+ :data => {}
+ )
+ end
+
+end
\ No newline at end of file
diff --git a/config/routes/online_notification.rb b/config/routes/online_notification.rb
new file mode 100644
index 000000000..5ecfe3303
--- /dev/null
+++ b/config/routes/online_notification.rb
@@ -0,0 +1,8 @@
+Zammad::Application.routes.draw do
+ api_path = Rails.configuration.api_path
+
+ # groups
+ match api_path + '/online_notifications', :to => 'online_notifications#index', :via => :get
+ match api_path + '/online_notifications/:id', :to => 'online_notifications#update', :via => :put
+
+end
\ No newline at end of file
diff --git a/db/migrate/20130201071513_create_sla.rb b/db/migrate/20130201071513_create_sla.rb
index e70b50af4..0d570f249 100644
--- a/db/migrate/20130201071513_create_sla.rb
+++ b/db/migrate/20130201071513_create_sla.rb
@@ -17,4 +17,4 @@ class CreateSla < ActiveRecord::Migration
def down
end
-end
+end
\ No newline at end of file
diff --git a/db/migrate/20140824000002_create_online_notification.rb b/db/migrate/20140824000002_create_online_notification.rb
new file mode 100644
index 000000000..101564a20
--- /dev/null
+++ b/db/migrate/20140824000002_create_online_notification.rb
@@ -0,0 +1,17 @@
+class CreateOnlineNotification < ActiveRecord::Migration
+ def up
+ create_table :online_notifications do |t|
+ t.column :o_id, :integer, :null => false
+ t.column :object_lookup_id, :integer, :null => false
+ t.column :type_lookup_id, :integer, :null => false
+ t.column :user_id, :integer, :null => false
+ t.column :seen, :boolean, :null => false, :default => false
+ t.column :created_by_id, :integer, :null => false
+ t.timestamps
+ end
+ add_index :online_notifications, [:user_id]
+ end
+
+ def down
+ end
+end