Added online notification feature.
This commit is contained in:
parent
4c8a13d5b2
commit
1daf79041d
17 changed files with 460 additions and 44 deletions
|
@ -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) )
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,6 +27,7 @@ class App.OrganizationZoom extends App.Controller
|
|||
'#organization/zoom/' + @organization_id
|
||||
|
||||
activate: =>
|
||||
App.OnlineNotification.seen( 'Organization', @organization_id )
|
||||
@navupdate '#'
|
||||
|
||||
changed: =>
|
||||
|
|
|
@ -53,6 +53,7 @@ class App.TicketZoom extends App.Controller
|
|||
'#ticket/zoom/' + @ticket_id
|
||||
|
||||
activate: =>
|
||||
App.OnlineNotification.seen( 'Ticket', @ticket_id )
|
||||
@navupdate '#'
|
||||
|
||||
changed: =>
|
||||
|
|
|
@ -27,6 +27,7 @@ class App.UserZoom extends App.Controller
|
|||
'#user/zoom/' + @user_id
|
||||
|
||||
activate: =>
|
||||
App.OnlineNotification.seen( 'User', @user_id )
|
||||
@navupdate '#'
|
||||
|
||||
changed: =>
|
||||
|
|
|
@ -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('<div class="activity-counter">' + count.toString() + '</div>')
|
||||
|
||||
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' ) + " <span>#{counter}</span>"
|
||||
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)
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -5,9 +5,7 @@
|
|||
<div class="close icon"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="logo" title="<%- @C( 'product_name' ) %>">
|
||||
<div class="activity-counter">12</div>
|
||||
</div>
|
||||
<div class="logo" title="<%- @C( 'product_name' ) %>"></div>
|
||||
<ul id="global-search-result" class="custom-dropdown-menu" role="menu">
|
||||
</ul>
|
||||
</form>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<% for item in @items: %>
|
||||
<div class="activity-entry horizontal">
|
||||
<a class="activity-avatar" href="<%- item.created_by.uiUrl() %>">
|
||||
<span class="avatar" style="background-image: url(<%- item.created_by.imageUrl %>)"></span>
|
||||
</a>
|
||||
<a href="<%- item.link %>" class="activity-body flex horizontal">
|
||||
<span class="activity-message flex">
|
||||
<span class="activity-text <% if item.seen: %>inactive<% end %>">
|
||||
<%= item.created_by.displayName() %> <%- @T( item.type ) %> <%- @T( item.object_name ) %><% if item.title: %> (<%= item.title %>)<% end %>
|
||||
</span>
|
||||
<span class="activity-time <% if item.seen: %>inactive<% end %> humanTimeFromNow" data-time="<%- item.created_at %>">?</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<% end %>
|
91
app/controllers/online_notifications_controller.rb
Normal file
91
app/controllers/online_notifications_controller.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
144
app/models/online_notification.rb
Normal file
144
app/models/online_notification.rb
Normal file
|
@ -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
|
8
config/routes/online_notification.rb
Normal file
8
config/routes/online_notification.rb
Normal file
|
@ -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
|
|
@ -17,4 +17,4 @@ class CreateSla < ActiveRecord::Migration
|
|||
|
||||
def down
|
||||
end
|
||||
end
|
||||
end
|
17
db/migrate/20140824000002_create_online_notification.rb
Normal file
17
db/migrate/20140824000002_create_online_notification.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue