Improved activity stream messages.

This commit is contained in:
Martin Edenhofer 2016-02-22 20:58:23 +01:00
parent 2c6a1c542a
commit 10f13cb618
21 changed files with 163 additions and 62 deletions

View file

@ -482,6 +482,7 @@ class App.Controller extends Spine.Controller
# lookup real data # lookup real data
if App[item.object] && App[item.object].exists(item.o_id) if App[item.object] && App[item.object].exists(item.o_id)
object = App[item.object].find(item.o_id) object = App[item.object].find(item.o_id)
item.objectNative = object
item.link = object.uiUrl() item.link = object.uiUrl()
item.title = object.displayName() item.title = object.displayName()
item.object_name = object.objectDisplayName() item.object_name = object.objectDisplayName()

View file

@ -40,6 +40,11 @@ class App.OnlineNotificationWidget extends App.Controller
@createContainer() @createContainer()
@subscribeId = App.OnlineNotification.subscribe(@updateContent) @subscribeId = App.OnlineNotification.subscribe(@updateContent)
@bind('ui:rerender', =>
@updateContent()
'popover'
)
release: -> release: ->
@removeContainer() @removeContainer()
$(window).off 'click.notifications' $(window).off 'click.notifications'
@ -141,8 +146,10 @@ class App.OnlineNotificationWidget extends App.Controller
if !@alreadyShown[item.id] if !@alreadyShown[item.id]
@alreadyShown[item.id] = true @alreadyShown[item.id] = true
if @fetchedData if @fetchedData
word = "#{item.type}d" if item.objectNative && item.objectNative.activityMessage
title = "#{item.created_by.displayName()} #{App.i18n.translateInline(word)} #{App.i18n.translateInline(item.object_name)} \"#{item.title}\"" title = item.objectNative.activityMessage(item)
else
title = "Need objectNative in item #{item.object}.find(#{item.o_id})"
@notifyDesktop( @notifyDesktop(
url: item.link url: item.link
title: title title: title

View file

@ -174,7 +174,7 @@ class _i18nSingleton extends Spine.Module
translateInline: (string, args) => translateInline: (string, args) =>
return string if !string return string if !string
App.Utils.htmlEscape(@translate(string, args)) @translate(string, args, true)
translateContent: (string, args) => translateContent: (string, args) =>
return string if !string return string if !string
@ -182,19 +182,12 @@ class _i18nSingleton extends Spine.Module
if App.Config.get('translation_inline') if App.Config.get('translation_inline')
return '<span class="translation" onclick="arguments[0].stopPropagation(); return false" contenteditable="true" title="' + App.Utils.htmlEscape(string) + '">' + App.Utils.htmlEscape(@translate(string)) + '</span>' return '<span class="translation" onclick="arguments[0].stopPropagation(); return false" contenteditable="true" title="' + App.Utils.htmlEscape(string) + '">' + App.Utils.htmlEscape(@translate(string)) + '</span>'
translated = App.Utils.htmlEscape(@translate(string, args)) translated = @translate(string, args, true, true)
# apply inline markup
translated
.replace(/\|\|(.+?)\|\|/gm, '<i>$1</i>')
.replace(/\|(.+?)\|/gm, '<b>$1</b>')
.replace(/_(.+?)_/gm, '<u>$1</u>')
.replace(/§(.+?)§/gm, '<kbd>$1</kbd>')
translatePlain: (string, args) => translatePlain: (string, args) =>
@translate(string, args) @translate(string, args)
translate: (string, args) => translate: (string, args, quote, markup) =>
# type convertation # type convertation
if typeof string isnt 'string' if typeof string isnt 'string'
@ -221,10 +214,27 @@ class _i18nSingleton extends Spine.Module
@log 'notice', "translation for '#{string}' in '#{@locale}' is missing" @log 'notice', "translation for '#{string}' in '#{@locale}' is missing"
@_notTranslated[@locale][string] = true @_notTranslated[@locale][string] = true
# apply html quote
if quote
translated = App.Utils.htmlEscape(translated)
# apply inline markup
if markup
translated = translated
.replace(/\|\|(.+?)\|\|/gm, '<i>$1</i>')
.replace(/\|(.+?)\|/gm, '<b>$1</b>')
.replace(/_(.+?)_/gm, '<u>$1</u>')
.replace(/§(.+?)§/gm, '<kbd>$1</kbd>')
# search %s # search %s
if args if args
for arg in args for arg in args
translated = translated.replace(/%s/, arg) if quote
argNew = App.Utils.htmlEscape(arg)
else
argNew = arg
translated = translated.replace(/%s/, argNew)
@log 'debug', 'translate', string, args, translated @log 'debug', 'translate', string, args, translated

View file

@ -643,3 +643,6 @@ class App.Model extends Spine.Model
return return
) )
collection collection
activityMessage: (item) ->
return "Need own activityMessage() in model to generate text (#{@objectDisplayName()}/#{item.type})."

View file

@ -20,3 +20,10 @@ class App.Group extends App.Model
uiUrl: -> uiUrl: ->
'#group/zoom/' + @id '#group/zoom/' + @id
activityMessage: (item) ->
if item.type is 'create'
return App.i18n.translateContent('%s created Group |%s|', item.created_by.displayName(), item.title)
else if item.type is 'update'
return App.i18n.translateContent('%s updated Group |%s|', item.created_by.displayName(), item.title)
return "Unknow action for (#{@objectDisplayName()}/#{item.type}), extend activityMessage() of model."

View file

@ -45,3 +45,10 @@ Mit **Organisationen** können Sie Kunden **gruppieren**. Dies hat u. a. zwei be
class: 'organization organization-popover' class: 'organization organization-popover'
url: @uiUrl() url: @uiUrl()
icon: 'organization' icon: 'organization'
activityMessage: (item) ->
if item.type is 'create'
return App.i18n.translateContent('%s created Organization |%s|', item.created_by.displayName(), item.title)
else if item.type is 'update'
return App.i18n.translateContent('%s updated Organization |%s|', item.created_by.displayName(), item.title)
return "Unknow action for (#{@objectDisplayName()}/#{item.type}), extend activityMessage() of model."

View file

@ -14,3 +14,10 @@ class App.Role extends App.Model
@configure_overview = [ @configure_overview = [
'name', 'name',
] ]
activityMessage: (item) ->
if item.type is 'create'
return App.i18n.translateContent('%s created Role |%s|', item.created_by.displayName(), item.title)
else if item.type is 'update'
return App.i18n.translateContent('%s updated Role |%s|', item.created_by.displayName(), item.title)
return "Unknow action for (#{@objectDisplayName()}/#{item.type}), extend activityMessage() of model."

View file

@ -77,3 +77,14 @@ class App.Ticket extends App.Model
url: @uiUrl() url: @uiUrl()
icon: 'task-state' icon: 'task-state'
iconClass: @getState() iconClass: @getState()
activityMessage: (item) ->
if item.type is 'create'
return App.i18n.translateContent('%s created Ticket |%s|', item.created_by.displayName(), item.title)
else if item.type is 'update'
return App.i18n.translateContent('%s updated Ticket |%s|', item.created_by.displayName(), item.title)
else if item.type is 'reminder_reached'
return App.i18n.translateContent('Pending reminder reached for Ticket |%s|', item.title)
else if item.type is 'escalation'
return App.i18n.translateContent('Ticket |%s| is escalated!', item.title)
return "Unknow action for (#{@objectDisplayName()}/#{item.type}), extend activityMessage() of model."

View file

@ -38,3 +38,10 @@ class App.TicketArticle extends App.Model
if ticket.owner_id == user.id if ticket.owner_id == user.id
return 'important' return 'important'
'' ''
activityMessage: (item) ->
if item.type is 'create'
return App.i18n.translateContent('%s created Article for |%s|', item.created_by.displayName(), item.title)
else if item.type is 'update'
return App.i18n.translateContent('%s updated Article for |%s|', item.created_by.displayName(), item.title)
return "Unknow action for (#{@objectDisplayName()}/#{item.type}), extend activityMessage() of model."

View file

@ -142,3 +142,22 @@ class App.User extends App.Model
class: 'user user-popover' class: 'user user-popover'
url: @uiUrl() url: @uiUrl()
icon: 'user' icon: 'user'
activityMessage: (item) ->
if item.type is 'create'
return App.i18n.translateContent('%s created User |%s|', item.created_by.displayName(), item.title)
else if item.type is 'update'
return App.i18n.translateContent('%s updated User |%s|', item.created_by.displayName(), item.title)
else if item.type is 'session started'
return App.i18n.translateContent('%s started a new session', item.created_by.displayName())
else if item.type is 'switch to'
to = item.title
if item.objectNative
to = item.objectNative.displayName()
return App.i18n.translateContent('%s switched to |%s|!', item.created_by.displayName(), to)
else if item.type is 'ended switch to'
to = item.title
if item.objectNative
to = item.objectNative.displayName()
return App.i18n.translateContent('%s ended switch to |%s|!', item.created_by.displayName(), to)
return "Unknow action for (#{@objectDisplayName()}/#{item.type}), extend activityMessage() of model."

View file

@ -3,7 +3,11 @@
<a href="<%- @item.link %>" class="activity-body"> <a href="<%- @item.link %>" class="activity-body">
<span class="activity-message"> <span class="activity-message">
<span class="activity-text"> <span class="activity-text">
<%= @item.created_by.displayName() %> <%- @T( @item.type ) %> <%- @T( @item.object_name ) %><% if @item.title: %> <strong><%= @item.title %></strong><% end %> <% if @item.objectNative && @item.objectNative.activityMessage: %>
<%- @item.objectNative.activityMessage(@item) %>
<% else: %>
Need objectNative in item <%= item.object %>.find(<%= item.o_id %>)
<% end %>
</span> </span>
<%- @humanTime(@item.created_at, false, 'activity-time') %> <%- @humanTime(@item.created_at, false, 'activity-time') %>
</span> </span>

View file

@ -7,7 +7,11 @@
<div class="activity-body"> <div class="activity-body">
<a class="activity-message js-locationVerify" href="<%- item.link %>"> <a class="activity-message js-locationVerify" href="<%- item.link %>">
<span class="activity-text"> <span class="activity-text">
<%= item.created_by.displayName() %> <%- @T( "#{item.type}d" ) %> <%- @T( item.object_name ) %><% if item.title: %> <strong><%= item.title %></strong><% end %> <% if item.objectNative && item.objectNative.activityMessage: %>
<%- item.objectNative.activityMessage(item) %>
<% else: %>
Need objectNative in item <%= item.object %>.find(<%= item.o_id %>)
<% end %>
</span> </span>
<%- @humanTime(item.created_at, false, 'activity-time') %> <%- @humanTime(item.created_at, false, 'activity-time') %>
</a> </a>

View file

@ -10,12 +10,12 @@ class ActivityStream < ApplicationModel
add a new activity entry for an object add a new activity entry for an object
ActivityStream.add( ActivityStream.add(
:type => 'updated', type: 'update',
:object => 'Ticket', object: 'Ticket',
:role => 'Admin', role: 'Admin',
:o_id => ticket.id, o_id: ticket.id,
:created_by_id => 1, created_by_id: 1,
:created_at => '2013-06-04 10:00:00', created_at: '2013-06-04 10:00:00',
) )
=end =end
@ -49,7 +49,7 @@ add a new activity entry for an object
).order('created_at DESC, id DESC').first ).order('created_at DESC, id DESC').first
# resturn if old entry is really fresh # resturn if old entry is really fresh
return result if result && result.created_at.to_i >= ( data[:created_at].to_i - 12 ) return result if result && result.created_at.to_i >= ( data[:created_at].to_i - 20 )
# create history # create history
record = { record = {

View file

@ -826,7 +826,7 @@ log object create activity stream, if configured - will be executed automaticall
def activity_stream_create def activity_stream_create
return if !self.class.activity_stream_support_config return if !self.class.activity_stream_support_config
activity_stream_log('created', self['created_by_id']) activity_stream_log('create', self['created_by_id'])
end end
=begin =begin
@ -867,7 +867,7 @@ log object update activity stream, if configured - will be executed automaticall
return if !log return if !log
activity_stream_log('updated', self['updated_by_id']) activity_stream_log('update', self['updated_by_id'])
end end
=begin =begin

View file

@ -6,10 +6,10 @@ module ApplicationModel::ActivityStreamBase
log activity for this object log activity for this object
article = Ticket::Article.find(123) article = Ticket::Article.find(123)
result = article.activity_stream_log( 'created', user_id ) result = article.activity_stream_log('create', user_id)
# force log # force log
result = article.activity_stream_log( 'created', user_id, true ) result = article.activity_stream_log('create', user_id, true)
returns returns

View file

@ -151,9 +151,12 @@ class Observer::Ticket::Notification::BackgroundJob
if channels['online'] if channels['online']
used_channels.push 'online' used_channels.push 'online'
created_by_id = ticket.updated_by_id || 1
# delete old notifications # delete old notifications
if @p[:type] == 'reminder_reached' || @p[:type] == 'escalation' if @p[:type] == 'reminder_reached' || @p[:type] == 'escalation'
seen = false seen = false
created_by_id = 1
OnlineNotification.remove_by_type('Ticket', ticket.id, @p[:type], user) OnlineNotification.remove_by_type('Ticket', ticket.id, @p[:type], user)
# on updates without state changes create unseen messages # on updates without state changes create unseen messages
@ -168,7 +171,7 @@ class Observer::Ticket::Notification::BackgroundJob
object: 'Ticket', object: 'Ticket',
o_id: ticket.id, o_id: ticket.id,
seen: seen, seen: seen,
created_by_id: ticket.updated_by_id || 1, created_by_id: created_by_id,
user_id: user.id, user_id: user.id,
) )
Rails.logger.debug "sent ticket online notifiaction to agent (#{@p[:type]}/#{ticket.id}/#{user.email})" Rails.logger.debug "sent ticket online notifiaction to agent (#{@p[:type]}/#{ticket.id}/#{user.email})"

View file

@ -6,7 +6,7 @@ module Ticket::ActivityStreamLog
log activity for this object log activity for this object
ticket = Ticket.find(123) ticket = Ticket.find(123)
result = ticket.activity_stream_log( 'created', user_id ) result = ticket.activity_stream_log('create', user_id)
returns returns

View file

@ -6,7 +6,7 @@ module Ticket::Article::ActivityStreamLog
log activity for this object log activity for this object
article = Ticket::Article.find(123) article = Ticket::Article.find(123)
result = article.activity_stream_log( 'created', user_id ) result = article.activity_stream_log('create', user_id)
returns returns

View file

@ -0,0 +1,5 @@
class ImprovedActivityMessages < ActiveRecord::Migration
def up
ActivityStream.destroy_all
end
end

View file

@ -271,8 +271,11 @@ test( "i18n", function() {
translated = App.i18n.translateContent('%s ago', 123); translated = App.i18n.translateContent('%s ago', 123);
equal( translated, 'vor 123', 'de-de - %s' ); equal( translated, 'vor 123', 'de-de - %s' );
translated = App.i18n.translateContent('%s %s test', 123, 'xxx'); translated = App.i18n.translateContent('%s ago', '<b>quote</b>');
equal( translated, '123 xxx test', 'de-de - %s %s' ); equal( translated, 'vor &lt;b&gt;quote&lt;/b&gt;', 'de-de - %s - quote' );
translated = App.i18n.translateContent('%s %s test', 123, 'xxx |B|');
equal( translated, '123 xxx |B| test', 'de-de - %s %s' );
translated = App.i18n.translateContent('|%s| %s test', 123, 'xxx'); translated = App.i18n.translateContent('|%s| %s test', 123, 'xxx');
equal( translated, '<b>123</b> xxx test', 'de-de - *%s* %s' ); equal( translated, '<b>123</b> xxx test', 'de-de - *%s* %s' );
@ -311,11 +314,14 @@ test( "i18n", function() {
translated = App.i18n.translateContent('%s ago', 123); translated = App.i18n.translateContent('%s ago', 123);
equal( translated, '123 ago', 'en-us - %s' ); equal( translated, '123 ago', 'en-us - %s' );
translated = App.i18n.translateContent('%s ago', '<b>quote</b>');
equal( translated, '&lt;b&gt;quote&lt;/b&gt; ago', 'en-us - %s - qupte' );
translated = App.i18n.translateContent('%s %s test', 123, 'xxx'); translated = App.i18n.translateContent('%s %s test', 123, 'xxx');
equal( translated, '123 xxx test', 'en-us - %s %s' ); equal( translated, '123 xxx test', 'en-us - %s %s' );
translated = App.i18n.translateContent('|%s| %s test', 123, 'xxx'); translated = App.i18n.translateContent('|%s| %s test', 123, 'xxx |B|');
equal( translated, '<b>123</b> xxx test', 'en-us - *%s* %s' ); equal( translated, '<b>123</b> xxx |B| test', 'en-us - *%s* %s' );
translated = App.i18n.translateContent('||%s|| %s test', 123, 'xxx'); translated = App.i18n.translateContent('||%s|| %s test', 123, 'xxx');
equal( translated, '<i>123</i> xxx test', 'en-us - *%s* %s' ); equal( translated, '<i>123</i> xxx test', 'en-us - *%s* %s' );

View file

@ -61,22 +61,22 @@ class ActivityStreamTest < ActiveSupport::TestCase
{ {
result: true, result: true,
object: 'Ticket', object: 'Ticket',
type: 'updated', type: 'update',
}, },
{ {
result: true, result: true,
object: 'Ticket::Article', object: 'Ticket::Article',
type: 'created', type: 'create',
}, },
{ {
result: true, result: true,
object: 'Ticket', object: 'Ticket',
type: 'created', type: 'create',
}, },
{ {
result: false, result: false,
object: 'User', object: 'User',
type: 'updated', type: 'update',
o_id: current_user.id, o_id: current_user.id,
}, },
] ]
@ -118,7 +118,7 @@ class ActivityStreamTest < ActiveSupport::TestCase
article.update_attributes(test[:update][:article]) article.update_attributes(test[:update][:article])
end end
sleep 15 sleep 21
if test[:update][:ticket] if test[:update][:ticket]
ticket.update_attributes(test[:update][:ticket]) ticket.update_attributes(test[:update][:ticket])
end end
@ -168,12 +168,12 @@ class ActivityStreamTest < ActiveSupport::TestCase
{ {
result: true, result: true,
object: 'Organization', object: 'Organization',
type: 'updated', type: 'update',
}, },
{ {
result: true, result: true,
object: 'Organization', object: 'Organization',
type: 'created', type: 'create',
}, },
] ]
}, },
@ -194,7 +194,7 @@ class ActivityStreamTest < ActiveSupport::TestCase
test[:check][1][:o_id] = organization.id test[:check][1][:o_id] = organization.id
test[:check][1][:updated_at] = organization.updated_at test[:check][1][:updated_at] = organization.updated_at
test[:check][1][:created_by_id] = current_user.id test[:check][1][:created_by_id] = current_user.id
sleep 13 sleep 19
end end
if test[:update2][:organization] if test[:update2][:organization]
@ -240,12 +240,12 @@ class ActivityStreamTest < ActiveSupport::TestCase
{ {
result: true, result: true,
object: 'User', object: 'User',
type: 'created', type: 'create',
}, },
{ {
result: false, result: false,
object: 'User', object: 'User',
type: 'updated', type: 'update',
}, },
] ]
}, },
@ -312,12 +312,12 @@ class ActivityStreamTest < ActiveSupport::TestCase
{ {
result: true, result: true,
object: 'User', object: 'User',
type: 'updated', type: 'update',
}, },
{ {
result: true, result: true,
object: 'User', object: 'User',
type: 'created', type: 'create',
}, },
] ]
}, },
@ -340,7 +340,7 @@ class ActivityStreamTest < ActiveSupport::TestCase
end end
# to verify update which need to be logged # to verify update which need to be logged
sleep 14 sleep 21
if test[:update2][:user] if test[:update2][:user]
user.update_attributes(test[:update2][:user]) user.update_attributes(test[:update2][:user])