Implemented issue #352 - Show other active agents in ticket detail view (collision prevention).
This commit is contained in:
parent
c6346ea210
commit
f9fdbe5934
20 changed files with 678 additions and 49 deletions
|
@ -94,10 +94,10 @@ class Index extends App.ControllerSubContent
|
||||||
# update avatar text if needed
|
# update avatar text if needed
|
||||||
if activeAvatar.text()
|
if activeAvatar.text()
|
||||||
replaceAvatar.text(activeAvatar.text())
|
replaceAvatar.text(activeAvatar.text())
|
||||||
replaceAvatar.addClass('unique')
|
replaceAvatar.addClass('avatar--unique')
|
||||||
else
|
else
|
||||||
replaceAvatar.text('')
|
replaceAvatar.text('')
|
||||||
replaceAvatar.removeClass('unique')
|
replaceAvatar.removeClass('avatar--unique')
|
||||||
)
|
)
|
||||||
avatar
|
avatar
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ class Content extends App.ControllerContent
|
||||||
width = 300
|
width = 300
|
||||||
height = 226
|
height = 226
|
||||||
|
|
||||||
holder.addClass 'unique'
|
holder.addClass 'avatar--unique'
|
||||||
|
|
||||||
rng = new Math.seedrandom(id)
|
rng = new Math.seedrandom(id)
|
||||||
x = rng() * (width - size)
|
x = rng() * (width - size)
|
||||||
|
|
|
@ -47,6 +47,13 @@ class App.TicketZoomAttributeBar extends App.Controller
|
||||||
resetButtonShown: resetButtonShown
|
resetButtonShown: resetButtonShown
|
||||||
))
|
))
|
||||||
@setSecondaryAction(@secondaryAction, localeEl)
|
@setSecondaryAction(@secondaryAction, localeEl)
|
||||||
|
|
||||||
|
if @permissionCheck('ticket.agent')
|
||||||
|
new App.TaskbarWatcher(
|
||||||
|
task_key: @task_key
|
||||||
|
el: localeEl.filter('.js-avatars')
|
||||||
|
)
|
||||||
|
|
||||||
@html localeEl
|
@html localeEl
|
||||||
|
|
||||||
checkMacroChanges: =>
|
checkMacroChanges: =>
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
class App.TaskbarWatcher extends App.Controller
|
||||||
|
constructor: ->
|
||||||
|
super
|
||||||
|
@subscribeId = App.TaskManager.preferencesSubscribe(@task_key, @render)
|
||||||
|
|
||||||
|
release: =>
|
||||||
|
return if !@subscribeId
|
||||||
|
App.TaskManager.preferencesUnsubscribe(@subscribeId)
|
||||||
|
|
||||||
|
render: (preferences) =>
|
||||||
|
return if !preferences
|
||||||
|
return if !preferences.tasks
|
||||||
|
return if !@diffrence(@lastTasks, preferences.tasks)
|
||||||
|
@lastTasks = preferences.tasks
|
||||||
|
|
||||||
|
watchers = []
|
||||||
|
currentUserId = App.Session.get('id')
|
||||||
|
@el.empty()
|
||||||
|
for watcher in preferences.tasks
|
||||||
|
if watcher.user_id != currentUserId
|
||||||
|
cssClass = []
|
||||||
|
lastContact = new Date(new Date(watcher.last_contact).getTime() + 5 * 60000)
|
||||||
|
if new Date() > lastContact
|
||||||
|
cssClass.push('avatar--idle')
|
||||||
|
if watcher.changed
|
||||||
|
cssClass.push('avatar--changed')
|
||||||
|
else
|
||||||
|
cssClass.push('avatar--not-changed')
|
||||||
|
|
||||||
|
@el.append('<div class="js-avatar"></div>')
|
||||||
|
@el.append('<div class="half-spacer"></div>')
|
||||||
|
|
||||||
|
avatar = new App.WidgetAvatar(
|
||||||
|
el: @el.find('.js-avatar').last()
|
||||||
|
object_id: watcher.user_id
|
||||||
|
size: 40
|
||||||
|
cssClass: cssClass.join(' ')
|
||||||
|
)
|
||||||
|
|
||||||
|
if watcher.changed
|
||||||
|
status = $('<div class="avatar-status"></div>')
|
||||||
|
status.append App.Utils.icon('pen')
|
||||||
|
avatar.el.find('.avatar').append status
|
||||||
|
|
||||||
|
diffrence: (lastTasks, newTasks) ->
|
||||||
|
return true if !lastTasks
|
||||||
|
return true if lastTasks.length != newTasks.length
|
||||||
|
for taskPosition of lastTasks
|
||||||
|
return true if !lastTasks[taskPosition] || !newTasks[taskPosition]
|
||||||
|
return true if lastTasks[taskPosition].user_id != newTasks[taskPosition].user_id
|
||||||
|
return true if lastTasks[taskPosition].changed != newTasks[taskPosition].changed
|
||||||
|
if lastTasks[taskPosition].last_contact
|
||||||
|
lastContact = new Date(new Date(lastTasks[taskPosition].last_contact).getTime() + 5 * 60000)
|
||||||
|
return true if new Date() > lastContact
|
||||||
|
false
|
|
@ -10,5 +10,5 @@ class App.WidgetAvatar extends App.ObserverController
|
||||||
globalRerender: false
|
globalRerender: false
|
||||||
|
|
||||||
render: (user) =>
|
render: (user) =>
|
||||||
@html(user.avatar @size, @position, undefined, false, false, @type)
|
@html(user.avatar(@size, @position, @cssClass, false, false, @type))
|
||||||
@userPopups(@position)
|
@userPopups(@position)
|
||||||
|
|
|
@ -71,6 +71,14 @@ class App.TaskManager
|
||||||
return if !_instance
|
return if !_instance
|
||||||
_instance.showControllerHideOthers()
|
_instance.showControllerHideOthers()
|
||||||
|
|
||||||
|
@preferencesSubscribe: (key, callback) ->
|
||||||
|
return if !_instance
|
||||||
|
_instance.preferencesSubscribe(key, callback)
|
||||||
|
|
||||||
|
@preferencesUnsubscribe: (id) ->
|
||||||
|
return if !_instance
|
||||||
|
_instance.preferencesUnsubscribe(id)
|
||||||
|
|
||||||
class _taskManagerSingleton extends App.Controller
|
class _taskManagerSingleton extends App.Controller
|
||||||
@include App.LogInclude
|
@include App.LogInclude
|
||||||
|
|
||||||
|
@ -83,12 +91,19 @@ class _taskManagerSingleton extends App.Controller
|
||||||
@offlineModus = params.offlineModus
|
@offlineModus = params.offlineModus
|
||||||
@tasksInitial()
|
@tasksInitial()
|
||||||
|
|
||||||
|
@bind('taskbar:preferences', (data) =>
|
||||||
|
@tasksPreferences[data.key] = data.preferences
|
||||||
|
@preferencesExecuteCallbacks(data.key)
|
||||||
|
)
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
@domStore = {}
|
@domStore = {}
|
||||||
@shownStore = {}
|
@shownStore = {}
|
||||||
@workers = {}
|
@workers = {}
|
||||||
@allTasksByKey = {}
|
@allTasksByKey = {}
|
||||||
@tasksToUpdate = {}
|
@tasksToUpdate = {}
|
||||||
|
@tasksPreferences = {}
|
||||||
|
@tasksPreferencesCallbacks = {}
|
||||||
@activeTaskHistory = []
|
@activeTaskHistory = []
|
||||||
@queue = []
|
@queue = []
|
||||||
@queueRunning = false
|
@queueRunning = false
|
||||||
|
@ -206,13 +221,21 @@ class _taskManagerSingleton extends App.Controller
|
||||||
|
|
||||||
# save new task and update task collection
|
# save new task and update task collection
|
||||||
ui = @
|
ui = @
|
||||||
|
@tasksToUpdate[params.key] = 'inCreate'
|
||||||
task.save(
|
task.save(
|
||||||
done: ->
|
done: ->
|
||||||
|
if ui.tasksToUpdate[params.key] is 'inCreate'
|
||||||
|
delete ui.tasksToUpdate[params.key]
|
||||||
ui.allTasksByKey[params.key] = @attributes()
|
ui.allTasksByKey[params.key] = @attributes()
|
||||||
|
ui.tasksPreferences[params.key] = clone(@preferences)
|
||||||
|
ui.preferencesExecuteCallbacks(params.key)
|
||||||
for taskPosition of ui.allTasks
|
for taskPosition of ui.allTasks
|
||||||
if ui.allTasks[taskPosition] && ui.allTasks[taskPosition]['key'] is @key
|
if ui.allTasks[taskPosition] && ui.allTasks[taskPosition]['key'] is @key
|
||||||
task = @attributes()
|
task = @attributes()
|
||||||
ui.allTasks[taskPosition] = task
|
ui.allTasks[taskPosition] = task
|
||||||
|
fail: ->
|
||||||
|
if ui.tasksToUpdate[params.key] is 'inCreate'
|
||||||
|
delete ui.tasksToUpdate[params.key]
|
||||||
)
|
)
|
||||||
|
|
||||||
# empty static content if task is shown
|
# empty static content if task is shown
|
||||||
|
@ -491,7 +514,8 @@ class _taskManagerSingleton extends App.Controller
|
||||||
|
|
||||||
taskUpdate: (task, mute = false) ->
|
taskUpdate: (task, mute = false) ->
|
||||||
@log 'debug', 'UPDATE task', task, mute
|
@log 'debug', 'UPDATE task', task, mute
|
||||||
@tasksToUpdate[ task.key ] = 'toUpdate'
|
return if @tasksToUpdate[task.key] is 'inCreate'
|
||||||
|
@tasksToUpdate[task.key] = 'toUpdate'
|
||||||
@taskUpdateTrigger()
|
@taskUpdateTrigger()
|
||||||
return if mute
|
return if mute
|
||||||
@touch(task.key)
|
@touch(task.key)
|
||||||
|
@ -520,8 +544,10 @@ class _taskManagerSingleton extends App.Controller
|
||||||
continue if !key
|
continue if !key
|
||||||
task = @get(key)
|
task = @get(key)
|
||||||
continue if !task
|
continue if !task
|
||||||
if @tasksToUpdate[ task.key ] is 'toUpdate'
|
if @tasksToUpdate[task.key] is 'toUpdate'
|
||||||
@tasksToUpdate[ task.key ] = 'inProgress'
|
@tasksToUpdate[task.key] = 'inProgress'
|
||||||
|
taskUpdate = App.Taskbar.findByAttribute('key', task.key)
|
||||||
|
if !taskUpdate
|
||||||
taskUpdate = new App.Taskbar
|
taskUpdate = new App.Taskbar
|
||||||
taskUpdate.load(task)
|
taskUpdate.load(task)
|
||||||
if taskUpdate.isOnline()
|
if taskUpdate.isOnline()
|
||||||
|
@ -539,7 +565,7 @@ class _taskManagerSingleton extends App.Controller
|
||||||
taskDestroy: (task) ->
|
taskDestroy: (task) ->
|
||||||
|
|
||||||
# check if update is still in process
|
# check if update is still in process
|
||||||
if @tasksToUpdate[ task.key ] is 'inProgress'
|
if @tasksToUpdate[task.key] is 'inProgress' || @tasksToUpdate[task.key] is 'inCreate'
|
||||||
App.Delay.set(
|
App.Delay.set(
|
||||||
=> @taskDestroy(task)
|
=> @taskDestroy(task)
|
||||||
800
|
800
|
||||||
|
@ -550,12 +576,14 @@ class _taskManagerSingleton extends App.Controller
|
||||||
return
|
return
|
||||||
|
|
||||||
# destroy task in backend
|
# destroy task in backend
|
||||||
delete @tasksToUpdate[ task.key ]
|
delete @tasksToUpdate[task.key]
|
||||||
|
|
||||||
# if task isnt already stored on backend
|
# if task isnt already stored on backend
|
||||||
return if !task.id
|
return if !task.id
|
||||||
App.Taskbar.destroy(task.id)
|
App.Taskbar.destroy(task.id)
|
||||||
|
|
||||||
|
delete @tasksPreferences[task.key]
|
||||||
|
|
||||||
tasksAutoCleanupDelay: =>
|
tasksAutoCleanupDelay: =>
|
||||||
delay = =>
|
delay = =>
|
||||||
@tasksAutoCleanup()
|
@tasksAutoCleanup()
|
||||||
|
@ -637,3 +665,26 @@ class _taskManagerSingleton extends App.Controller
|
||||||
)
|
)
|
||||||
|
|
||||||
App.Event.trigger 'taskbar:ready'
|
App.Event.trigger 'taskbar:ready'
|
||||||
|
|
||||||
|
preferencesSubscribe: (key, callback) =>
|
||||||
|
if !@tasksPreferencesCallbacks[key]
|
||||||
|
@tasksPreferencesCallbacks[key] = {}
|
||||||
|
subscribeId = "#{key}#{Math.floor(Math.random() * 999999)}"
|
||||||
|
@tasksPreferencesCallbacks[key][subscribeId] = callback
|
||||||
|
subscribeId
|
||||||
|
|
||||||
|
preferencesUnsubscribe: (id) =>
|
||||||
|
return if !@tasksPreferencesCallbacks
|
||||||
|
for key, value of @tasksPreferencesCallbacks
|
||||||
|
for subscribeId, callback of value
|
||||||
|
if subscribeId == id
|
||||||
|
delete value[subscribeId]
|
||||||
|
for key, value of @tasksPreferencesCallbacks
|
||||||
|
if _.isEmpty(value)
|
||||||
|
delete @tasksPreferencesCallbacks[key]
|
||||||
|
|
||||||
|
preferencesExecuteCallbacks: (key) =>
|
||||||
|
return if !@tasksPreferencesCallbacks[key]
|
||||||
|
return if !@tasksPreferences[key]
|
||||||
|
for subscribeId, callback of @tasksPreferencesCallbacks[key]
|
||||||
|
callback(@tasksPreferences[key])
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<span class="avatar <%- @cssClass %> unique" style="background-position: -<%- @x %>px -<%- @y %>px;"<%- @placement %><%- @data %>>
|
<span class="avatar <%- @cssClass %> avatar--unique" style="background-position: -<%- @x %>px -<%- @y %>px;"<%- @placement %><%- @data %>>
|
||||||
<%- @Icon('crown') if @vip %>
|
<%- @Icon('crown') if @vip %>
|
||||||
<%= @initials %>
|
<%= @initials %>
|
||||||
</span>
|
</span>
|
|
@ -32,7 +32,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="flex vertical center">
|
<div class="flex vertical center">
|
||||||
<span class="avatar unique user-popover size-50" data-id="2" style="background-position: -92.79607555375712px -106.24902447601627px;" data-original-title="" title="">NB</span>
|
<span class="avatar avatar--unique user-popover size-50" data-id="2" style="background-position: -92.79607555375712px -106.24902447601627px;" data-original-title="" title="">NB</span>
|
||||||
<div class="ticket-title">
|
<div class="ticket-title">
|
||||||
<h1 contenteditable="true" class="ticket-title-update" data-placeholder="Enter Title...">Welcome to Zammad! We want to entertain you and your whole family!</h1>
|
<h1 contenteditable="true" class="ticket-title-update" data-placeholder="Enter Title...">Welcome to Zammad! We want to entertain you and your whole family!</h1>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="article-content">
|
<div class="article-content">
|
||||||
<span class="avatar unique user-popover " data-id="2" style="background-position: -96.5079185759074px -112.28590086669901px;" data-placement="left" data-original-title="" title="">NB</span>
|
<span class="avatar avatar--unique user-popover " data-id="2" style="background-position: -96.5079185759074px -112.28590086669901px;" data-placement="left" data-original-title="" title="">NB</span>
|
||||||
<div class="flex bubble-gap internal-border">
|
<div class="flex bubble-gap internal-border">
|
||||||
<div class="textBubble">
|
<div class="textBubble">
|
||||||
<div class="bubble-arrow"></div>
|
<div class="bubble-arrow"></div>
|
||||||
|
@ -155,7 +155,7 @@ The <a href="https://zammad.org" title="https://zammad.org" target="_blank">zamm
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="article-content">
|
<div class="article-content">
|
||||||
<span class="avatar unique user-popover " data-id="2" style="background-position: -96.5079185759074px -112.28590086669901px;" data-placement="left" data-original-title="" title="">NB</span>
|
<span class="avatar avatar--unique user-popover " data-id="2" style="background-position: -96.5079185759074px -112.28590086669901px;" data-placement="left" data-original-title="" title="">NB</span>
|
||||||
<div class="flex bubble-gap internal-border">
|
<div class="flex bubble-gap internal-border">
|
||||||
<div class="textBubble"><div class="bubble-arrow"></div>Ich wollte mir die Lyrics von Maria herunterladen, aber ich schaff es einfach nicht, da raufzukommen. Schick mir bitte mein Passwort.
|
<div class="textBubble"><div class="bubble-arrow"></div>Ich wollte mir die Lyrics von Maria herunterladen, aber ich schaff es einfach nicht, da raufzukommen. Schick mir bitte mein Passwort.
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<div class="ticketZoom-header">
|
<div class="ticketZoom-header">
|
||||||
<div class="flex vertical center">
|
<div class="flex vertical center">
|
||||||
<div class="js-avatar">
|
<div class="js-avatar">
|
||||||
<span class="avatar unique size-50 user-popover" data-id="2" data-original-title="" style="background-position: -92.79607555375712px -106.24902447601627px;" title="">NB</span>
|
<span class="avatar avatar--unique size-50 user-popover" data-id="2" data-original-title="" style="background-position: -92.79607555375712px -106.24902447601627px;" title="">NB</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ticket-title">
|
<div class="ticket-title">
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
|
|
||||||
<div class="article-content">
|
<div class="article-content">
|
||||||
<div class="js-avatar">
|
<div class="js-avatar">
|
||||||
<span class="avatar unique size-40 user-popover" data-id="2" data-original-title="" style="background-position: -96.5079185759074px -112.28590086669901px;" title="">NB</span>
|
<span class="avatar avatar--unique size-40 user-popover" data-id="2" data-original-title="" style="background-position: -96.5079185759074px -112.28590086669901px;" title="">NB</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bubble-gap">
|
<div class="bubble-gap">
|
||||||
|
@ -796,7 +796,7 @@
|
||||||
|
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<div class="sidebar-block">
|
<div class="sidebar-block">
|
||||||
<span class="avatar unique userInfo-avatar size-50 user-popover" data-id="2" style="background-position: -92.79607555375712px -106.24902447601627px;">NB</span>
|
<span class="avatar avatar--unique userInfo-avatar size-50 user-popover" data-id="2" style="background-position: -92.79607555375712px -106.24902447601627px;">NB</span>
|
||||||
|
|
||||||
<h3 title="Name">Nicole Braun</h3>
|
<h3 title="Name">Nicole Braun</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -862,7 +862,7 @@
|
||||||
|
|
||||||
<div class="userList">
|
<div class="userList">
|
||||||
<div class="userList-entry">
|
<div class="userList-entry">
|
||||||
<span class="avatar unique size-40 user-popover" data-id="2" data-original-title="" style="background-position: -96.5079185759074px -112.28590086669901px;" title="">NB</span> <a class="userList-entry user-popover" data-id="2" data-original-title="" href="#user/profile/2" title="">Nicole Braun</a>
|
<span class="avatar avatar--unique size-40 user-popover" data-id="2" data-original-title="" style="background-position: -96.5079185759074px -112.28590086669901px;" title="">NB</span> <a class="userList-entry user-popover" data-id="2" data-original-title="" href="#user/profile/2" title="">Nicole Braun</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<label><%- @T('Members') %></label>
|
<label><%- @T('Members') %></label>
|
||||||
<div class="profile-details horizontal wrap">
|
<div class="profile-details horizontal wrap">
|
||||||
<div class="profile-organizationMember">
|
<div class="profile-organizationMember">
|
||||||
<span class="avatar unique user-popover size-40" data-id="4" style="background-position: -97.5718417033075px -178.430732445616px;">AG</span>
|
<span class="avatar avatar--unique user-popover size-40" data-id="4" style="background-position: -97.5718417033075px -178.430732445616px;">AG</span>
|
||||||
<a href="#">Doreen Kubiak</a>
|
<a href="#">Doreen Kubiak</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-organizationMember">
|
<div class="profile-organizationMember">
|
||||||
|
@ -60,11 +60,11 @@
|
||||||
<a href="#">Franz Xaver</a>
|
<a href="#">Franz Xaver</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-organizationMember">
|
<div class="profile-organizationMember">
|
||||||
<span class="avatar unique user-popover size-40" data-id="4" style="background-position: -27.5718417033075px -17.430732445616px;">JM</span>
|
<span class="avatar avatar--unique user-popover size-40" data-id="4" style="background-position: -27.5718417033075px -17.430732445616px;">JM</span>
|
||||||
<a href="#">Julius Müller</a>
|
<a href="#">Julius Müller</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-organizationMember">
|
<div class="profile-organizationMember">
|
||||||
<span class="avatar unique user-popover size-40" data-id="4" style="background-position: -97.5718417033075px -178.430732445616px;">HH</span>
|
<span class="avatar avatar--unique user-popover size-40" data-id="4" style="background-position: -97.5718417033075px -178.430732445616px;">HH</span>
|
||||||
<a href="#">Hans Hubert</a>
|
<a href="#">Hans Hubert</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="flex vertical center">
|
<div class="flex vertical center">
|
||||||
<span class="avatar unique user-popover size-50" data-id="2" style="background-position: -92.79607555375712px -106.24902447601627px;" data-original-title="" title="">NB</span>
|
<span class="avatar avatar--unique user-popover size-50" data-id="2" style="background-position: -92.79607555375712px -106.24902447601627px;" data-original-title="" title="">NB</span>
|
||||||
<div class="ticket-title">
|
<div class="ticket-title">
|
||||||
<h1 contenteditable="true" class="ticket-title-update" data-placeholder="Enter Title...">Welcome to Zammad! We want to entertain you and your whole family!</h1>
|
<h1 contenteditable="true" class="ticket-title-update" data-placeholder="Enter Title...">Welcome to Zammad! We want to entertain you and your whole family!</h1>
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="article-content">
|
<div class="article-content">
|
||||||
<span class="avatar unique user-popover " data-id="2" style="background-position: -96.5079185759074px -112.28590086669901px;" data-placement="left" data-original-title="" title="">NB</span>
|
<span class="avatar avatar--unique user-popover " data-id="2" style="background-position: -96.5079185759074px -112.28590086669901px;" data-placement="left" data-original-title="" title="">NB</span>
|
||||||
<div class="flex bubble-gap internal-border">
|
<div class="flex bubble-gap internal-border">
|
||||||
<div class="textBubble">
|
<div class="textBubble">
|
||||||
<div class="bubble-arrow"></div>
|
<div class="bubble-arrow"></div>
|
||||||
|
@ -182,7 +182,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="article-content">
|
<div class="article-content">
|
||||||
<span class="avatar unique user-popover " data-id="2" style="background-position: -96.5079185759074px -112.28590086669901px;" data-placement="left" data-original-title="" title="">NB</span>
|
<span class="avatar avatar--unique user-popover " data-id="2" style="background-position: -96.5079185759074px -112.28590086669901px;" data-placement="left" data-original-title="" title="">NB</span>
|
||||||
<div class="flex bubble-gap internal-border">
|
<div class="flex bubble-gap internal-border">
|
||||||
<div class="textBubble"><div class="bubble-arrow"></div><div class="article-text" id="article-35098758344">Ich wollte mir die Lyrics von Maria herunterladen, aber ich schaff es einfach nicht, da raufzukommen. Schick mir bitte mein Passwort.
|
<div class="textBubble"><div class="bubble-arrow"></div><div class="article-text" id="article-35098758344">Ich wollte mir die Lyrics von Maria herunterladen, aber ich schaff es einfach nicht, da raufzukommen. Schick mir bitte mein Passwort.
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@
|
||||||
<hr>
|
<hr>
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<div class="sidebar-block">
|
<div class="sidebar-block">
|
||||||
<span class="avatar unique userInfo-avatar size-50 user-popover" data-id="2" style="background-position: -92.79607555375712px -106.24902447601627px;">NB</span>
|
<span class="avatar avatar--unique userInfo-avatar size-50 user-popover" data-id="2" style="background-position: -92.79607555375712px -106.24902447601627px;">NB</span>
|
||||||
<h3 title="Name">
|
<h3 title="Name">
|
||||||
Nicole Braun
|
Nicole Braun
|
||||||
</h3>
|
</h3>
|
||||||
|
@ -604,7 +604,7 @@
|
||||||
<label>Members</label>
|
<label>Members</label>
|
||||||
<div class="userList">
|
<div class="userList">
|
||||||
<div class="userList-entry">
|
<div class="userList-entry">
|
||||||
<span class="avatar unique size-40 user-popover" data-id="2" data-original-title="" style="background-position: -96.5079185759074px -112.28590086669901px;" title="">NB</span> <a class="userList-entry user-popover" data-id="2" data-original-title="" href="#user/profile/2" title="">Nicole Braun</a>
|
<span class="avatar avatar--unique size-40 user-popover" data-id="2" data-original-title="" style="background-position: -96.5079185759074px -112.28590086669901px;" title="">NB</span> <a class="userList-entry user-popover" data-id="2" data-original-title="" href="#user/profile/2" title="">Nicole Braun</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<div class="flex horizontal js-avatars"></div>
|
||||||
<div class="buttonDropdown btn js-reset <% if !@resetButtonShown: %>hide<% end %>"><%- @T('Discard your unsaved changes.') %></div>
|
<div class="buttonDropdown btn js-reset <% if !@resetButtonShown: %>hide<% end %>"><%- @T('Discard your unsaved changes.') %></div>
|
||||||
<div class="buttonDropdown dropdown dropdown--actions dropup">
|
<div class="buttonDropdown dropdown dropdown--actions dropup">
|
||||||
<div class="btn btn--text btn--icon--last" data-toggle="dropdown">
|
<div class="btn btn--text btn--icon--last" data-toggle="dropdown">
|
||||||
|
|
|
@ -3508,6 +3508,20 @@ footer {
|
||||||
fill: hsl(47,100%,59%);
|
fill: hsl(47,100%,59%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-status {
|
||||||
|
position: absolute;
|
||||||
|
right: -4px;
|
||||||
|
bottom: -4px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: hsl(234,10%,19%);
|
||||||
|
fill: white;
|
||||||
|
width: 21px;
|
||||||
|
height: 21px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
&.size-50 {
|
&.size-50 {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
|
@ -3530,7 +3544,12 @@ footer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.unique {
|
&--idle {
|
||||||
|
filter: grayscale(100%);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--unique {
|
||||||
background-image: image_url("/assets/images/avatar-bg.png");
|
background-image: image_url("/assets/images/avatar-bg.png");
|
||||||
background-size: 300px 226px;
|
background-size: 300px 226px;
|
||||||
color: white;
|
color: white;
|
||||||
|
@ -5236,6 +5255,10 @@ footer {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.userNotifications label + .btn {
|
.userNotifications label + .btn {
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
@ -8502,6 +8525,11 @@ body.fit {
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.half-spacer {
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.spacer {
|
.spacer {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
|
|
|
@ -4,16 +4,13 @@ class TaskbarController < ApplicationController
|
||||||
before_action :authentication_check
|
before_action :authentication_check
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
|
||||||
current_user_tasks = Taskbar.where(user_id: current_user.id)
|
current_user_tasks = Taskbar.where(user_id: current_user.id)
|
||||||
model_index_render_result(current_user_tasks)
|
model_index_render_result(current_user_tasks)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
taskbar = Taskbar.find(params[:id])
|
taskbar = Taskbar.find(params[:id])
|
||||||
access(taskbar)
|
access(taskbar)
|
||||||
|
|
||||||
model_show_render_item(taskbar)
|
model_show_render_item(taskbar)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -24,7 +21,6 @@ class TaskbarController < ApplicationController
|
||||||
def update
|
def update
|
||||||
taskbar = Taskbar.find(params[:id])
|
taskbar = Taskbar.find(params[:id])
|
||||||
access(taskbar)
|
access(taskbar)
|
||||||
|
|
||||||
taskbar.update_attributes!(Taskbar.param_cleanup(params))
|
taskbar.update_attributes!(Taskbar.param_cleanup(params))
|
||||||
model_update_render_item(taskbar)
|
model_update_render_item(taskbar)
|
||||||
end
|
end
|
||||||
|
@ -32,7 +28,6 @@ class TaskbarController < ApplicationController
|
||||||
def destroy
|
def destroy
|
||||||
taskbar = Taskbar.find(params[:id])
|
taskbar = Taskbar.find(params[:id])
|
||||||
access(taskbar)
|
access(taskbar)
|
||||||
|
|
||||||
taskbar.destroy
|
taskbar.destroy
|
||||||
model_destroy_render_item()
|
model_destroy_render_item()
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,16 +3,103 @@
|
||||||
class Taskbar < ApplicationModel
|
class Taskbar < ApplicationModel
|
||||||
store :state
|
store :state
|
||||||
store :params
|
store :params
|
||||||
before_create :update_last_contact, :set_user
|
store :preferences
|
||||||
before_update :update_last_contact, :set_user
|
before_create :update_last_contact, :set_user, :update_preferences_infos
|
||||||
|
before_update :update_last_contact, :set_user, :update_preferences_infos
|
||||||
|
|
||||||
|
after_update :notify_clients
|
||||||
|
after_destroy :update_preferences_infos, :notify_clients
|
||||||
|
|
||||||
|
attr_accessor :local_update
|
||||||
|
|
||||||
|
def state_changed?
|
||||||
|
return false if !state
|
||||||
|
return false if state.empty?
|
||||||
|
state.each { |_key, value|
|
||||||
|
if value.class == Hash || value.class == ActiveSupport::HashWithIndifferentAccess
|
||||||
|
value.each { |_key1, value1|
|
||||||
|
next if value1 && value1.empty?
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else
|
||||||
|
next if value && value.empty?
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def update_last_contact
|
def update_last_contact
|
||||||
|
return true if local_update
|
||||||
self.last_contact = Time.zone.now
|
self.last_contact = Time.zone.now
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_user
|
def set_user
|
||||||
|
return true if local_update
|
||||||
self.user_id = UserInfo.current_user_id
|
self.user_id = UserInfo.current_user_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_preferences_infos
|
||||||
|
return true if local_update
|
||||||
|
|
||||||
|
# find other same open tasks
|
||||||
|
preferences[:tasks] = []
|
||||||
|
Taskbar.where(key: key).order(:created_at).each { |taskbar|
|
||||||
|
changed = if taskbar.id == id
|
||||||
|
state_changed?
|
||||||
|
else
|
||||||
|
taskbar.state_changed?
|
||||||
|
end
|
||||||
|
task = {
|
||||||
|
id: taskbar.id,
|
||||||
|
user_id: taskbar.user_id,
|
||||||
|
last_contact: taskbar.last_contact,
|
||||||
|
changed: changed,
|
||||||
|
}
|
||||||
|
preferences[:tasks].push task
|
||||||
|
}
|
||||||
|
if !id
|
||||||
|
changed = state_changed?
|
||||||
|
task = {
|
||||||
|
user_id: user_id,
|
||||||
|
last_contact: last_contact,
|
||||||
|
changed: changed,
|
||||||
|
}
|
||||||
|
preferences[:tasks].push task
|
||||||
|
end
|
||||||
|
|
||||||
|
# update other taskbars
|
||||||
|
Taskbar.where(key: key).order(:created_at).each { |taskbar|
|
||||||
|
next if taskbar.id == id
|
||||||
|
taskbar.preferences = preferences
|
||||||
|
taskbar.local_update = true
|
||||||
|
taskbar.save!
|
||||||
|
}
|
||||||
|
|
||||||
|
return true if destroyed?
|
||||||
|
|
||||||
|
# remember preferences for current taskbar
|
||||||
|
self.preferences = preferences
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_clients
|
||||||
|
return true if !changes['preferences']
|
||||||
|
data = {
|
||||||
|
event: 'taskbar:preferences',
|
||||||
|
data: {
|
||||||
|
id: id,
|
||||||
|
key: key,
|
||||||
|
preferences: preferences,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
PushMessages.send_to(
|
||||||
|
user_id,
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -243,6 +243,7 @@ class CreateBase < ActiveRecord::Migration
|
||||||
t.string :key, limit: 100, null: false
|
t.string :key, limit: 100, null: false
|
||||||
t.string :callback, limit: 100, null: false
|
t.string :callback, limit: 100, null: false
|
||||||
t.text :state, limit: 20.megabytes + 1, null: true
|
t.text :state, limit: 20.megabytes + 1, null: true
|
||||||
|
t.text :preferences, limit: 5.megabytes + 1, null: true
|
||||||
t.string :params, limit: 2000, null: true
|
t.string :params, limit: 2000, null: true
|
||||||
t.integer :prio, null: false
|
t.integer :prio, null: false
|
||||||
t.boolean :notify, null: false, default: false
|
t.boolean :notify, null: false, default: false
|
||||||
|
@ -251,6 +252,7 @@ class CreateBase < ActiveRecord::Migration
|
||||||
end
|
end
|
||||||
add_index :taskbars, [:user_id]
|
add_index :taskbars, [:user_id]
|
||||||
add_index :taskbars, [:client_id]
|
add_index :taskbars, [:client_id]
|
||||||
|
add_index :taskbars, [:key]
|
||||||
|
|
||||||
create_table :tags do |t|
|
create_table :tags do |t|
|
||||||
t.references :tag_item, null: false
|
t.references :tag_item, null: false
|
||||||
|
|
11
db/migrate/20161228000001_add_taskbar_meta.rb
Normal file
11
db/migrate/20161228000001_add_taskbar_meta.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class AddTaskbarMeta < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.find_by(name: 'system_init_done')
|
||||||
|
|
||||||
|
add_column :taskbars, :preferences, :text, limit: 5.megabytes + 1, null: true
|
||||||
|
add_index :taskbars, [:key]
|
||||||
|
|
||||||
|
Cache.clear
|
||||||
|
end
|
||||||
|
end
|
|
@ -19,17 +19,31 @@ module PushMessages
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
Thread.current[:push_messages].push data
|
message = { type: 'broadcast', data: data }
|
||||||
|
Thread.current[:push_messages].push message
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.send_to(user_id, data)
|
||||||
|
if !PushMessages.enabled?
|
||||||
|
Sessions.send_to(user_id, data)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
message = { type: 'send_to', user_id: user_id, data: data }
|
||||||
|
Thread.current[:push_messages].push message
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.finish
|
def self.finish
|
||||||
return false if !enabled?
|
return false if !enabled?
|
||||||
Thread.current[:push_messages].each { |data|
|
Thread.current[:push_messages].each { |message|
|
||||||
|
if message[:type] == 'send_to'
|
||||||
|
Sessions.send_to(message[:user_id], message[:data])
|
||||||
|
else
|
||||||
Sessions.broadcast(
|
Sessions.broadcast(
|
||||||
data[:message],
|
message[:data][:message],
|
||||||
data[:type],
|
message[:data][:type],
|
||||||
data[:current_user_id],
|
message[:data][:current_user_id],
|
||||||
)
|
)
|
||||||
|
end
|
||||||
}
|
}
|
||||||
Thread.current[:push_messages] = nil
|
Thread.current[:push_messages] = nil
|
||||||
true
|
true
|
||||||
|
|
269
spec/models/taskbar_spec.rb
Normal file
269
spec/models/taskbar_spec.rb
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Taskbar do
|
||||||
|
|
||||||
|
context 'single creation' do
|
||||||
|
Taskbar.destroy_all
|
||||||
|
UserInfo.current_user_id = 1
|
||||||
|
|
||||||
|
taskbar = Taskbar.create(
|
||||||
|
client_id: 123,
|
||||||
|
key: 'Ticket-1234',
|
||||||
|
callback: 'TicketZoom',
|
||||||
|
params: {
|
||||||
|
id: 1234,
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
prio: 1,
|
||||||
|
notify: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
it 'existing key' do
|
||||||
|
expect(taskbar.key).to eq('Ticket-1234')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'params' do
|
||||||
|
expect(taskbar.params[:id]).to eq(1234)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'state' do
|
||||||
|
expect(taskbar.state.empty?).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
UserInfo.current_user_id = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'multible creation' do
|
||||||
|
|
||||||
|
it 'create tasks' do
|
||||||
|
|
||||||
|
Taskbar.destroy_all
|
||||||
|
UserInfo.current_user_id = 1
|
||||||
|
taskbar1 = Taskbar.create(
|
||||||
|
client_id: 123,
|
||||||
|
key: 'Ticket-1234',
|
||||||
|
callback: 'TicketZoom',
|
||||||
|
params: {
|
||||||
|
id: 1234,
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
prio: 1,
|
||||||
|
notify: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
UserInfo.current_user_id = 2
|
||||||
|
taskbar2 = Taskbar.create(
|
||||||
|
client_id: 123,
|
||||||
|
key: 'Ticket-1234',
|
||||||
|
callback: 'TicketZoom',
|
||||||
|
params: {
|
||||||
|
id: 1234,
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
prio: 2,
|
||||||
|
notify: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
taskbar1.reload
|
||||||
|
expect(taskbar1.preferences[:tasks].count).to eq(2)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar2.reload
|
||||||
|
expect(taskbar2.preferences[:tasks].count).to eq(2)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar3 = Taskbar.create(
|
||||||
|
client_id: 123,
|
||||||
|
key: 'Ticket-4444',
|
||||||
|
callback: 'TicketZoom',
|
||||||
|
params: {
|
||||||
|
id: 4444,
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
prio: 2,
|
||||||
|
notify: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
taskbar1.reload
|
||||||
|
expect(taskbar1.preferences[:tasks].count).to eq(2)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar2.reload
|
||||||
|
expect(taskbar2.preferences[:tasks].count).to eq(2)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar3.reload
|
||||||
|
expect(taskbar3.preferences[:tasks].count).to eq(1)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
|
||||||
|
UserInfo.current_user_id = 3
|
||||||
|
taskbar4 = Taskbar.create(
|
||||||
|
client_id: 123,
|
||||||
|
key: 'Ticket-1234',
|
||||||
|
callback: 'TicketZoom',
|
||||||
|
params: {
|
||||||
|
id: 1234,
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
prio: 4,
|
||||||
|
notify: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
taskbar1.reload
|
||||||
|
expect(taskbar1.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar1.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar2.reload
|
||||||
|
expect(taskbar2.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar2.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar3.reload
|
||||||
|
expect(taskbar3.preferences[:tasks].count).to eq(1)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar4.reload
|
||||||
|
expect(taskbar4.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar4.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar4.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar4.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
UserInfo.current_user_id = 2
|
||||||
|
taskbar2.state = { article: {}, ticket: {} }
|
||||||
|
taskbar2.save!
|
||||||
|
|
||||||
|
taskbar1.reload
|
||||||
|
expect(taskbar1.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar1.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar2.reload
|
||||||
|
expect(taskbar2.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar2.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar3.reload
|
||||||
|
expect(taskbar3.preferences[:tasks].count).to eq(1)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar4.reload
|
||||||
|
expect(taskbar4.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar4.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar4.preferences[:tasks][1][:changed]).to eq(false)
|
||||||
|
expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar4.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
UserInfo.current_user_id = 2
|
||||||
|
taskbar2.state = { article: { body: 'some body' }, ticket: {} }
|
||||||
|
taskbar2.save!
|
||||||
|
|
||||||
|
taskbar1.reload
|
||||||
|
expect(taskbar1.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:changed]).to eq(true)
|
||||||
|
expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar1.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar2.reload
|
||||||
|
expect(taskbar2.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:changed]).to eq(true)
|
||||||
|
expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar2.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar3.reload
|
||||||
|
expect(taskbar3.preferences[:tasks].count).to eq(1)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar4.reload
|
||||||
|
expect(taskbar4.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar4.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar4.preferences[:tasks][1][:changed]).to eq(true)
|
||||||
|
expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar4.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
UserInfo.current_user_id = 1
|
||||||
|
taskbar1.state = { article: { body: '' }, ticket: { state_id: 123 } }
|
||||||
|
taskbar1.save!
|
||||||
|
|
||||||
|
taskbar1.reload
|
||||||
|
expect(taskbar1.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar1.preferences[:tasks][0][:changed]).to eq(true)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar1.preferences[:tasks][1][:changed]).to eq(true)
|
||||||
|
expect(taskbar1.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar1.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar2.reload
|
||||||
|
expect(taskbar2.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar2.preferences[:tasks][0][:changed]).to eq(true)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar2.preferences[:tasks][1][:changed]).to eq(true)
|
||||||
|
expect(taskbar2.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar2.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar3.reload
|
||||||
|
expect(taskbar3.preferences[:tasks].count).to eq(1)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:user_id]).to eq(2)
|
||||||
|
expect(taskbar3.preferences[:tasks][0][:changed]).to eq(false)
|
||||||
|
|
||||||
|
taskbar4.reload
|
||||||
|
expect(taskbar4.preferences[:tasks].count).to eq(3)
|
||||||
|
expect(taskbar4.preferences[:tasks][0][:user_id]).to eq(1)
|
||||||
|
expect(taskbar4.preferences[:tasks][0][:changed]).to eq(true)
|
||||||
|
expect(taskbar4.preferences[:tasks][1][:user_id]).to eq(2)
|
||||||
|
expect(taskbar4.preferences[:tasks][1][:changed]).to eq(true)
|
||||||
|
expect(taskbar4.preferences[:tasks][2][:user_id]).to eq(3)
|
||||||
|
expect(taskbar4.preferences[:tasks][2][:changed]).to eq(false)
|
||||||
|
|
||||||
|
UserInfo.current_user_id = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -43,6 +43,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
value: 'some level 3 <b>body</b> 123äöü',
|
value: 'some level 3 <b>body</b> 123äöü',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
# change edit screen in instance 1
|
# change edit screen in instance 1
|
||||||
ticket_update(
|
ticket_update(
|
||||||
browser: browser1,
|
browser: browser1,
|
||||||
|
@ -58,6 +69,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
no_quote: true,
|
no_quote: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
# update ticket in instance 2
|
# update ticket in instance 2
|
||||||
ticket_update(
|
ticket_update(
|
||||||
browser: browser2,
|
browser: browser2,
|
||||||
|
@ -72,6 +94,16 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
value: '(Discard your unsaved changes.|Verwerfen der)',
|
value: '(Discard your unsaved changes.|Verwerfen der)',
|
||||||
no_quote: true,
|
no_quote: true,
|
||||||
)
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
click(
|
click(
|
||||||
browser: browser2,
|
browser: browser2,
|
||||||
|
@ -92,6 +124,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
# check content and edit screen in instance 1
|
# check content and edit screen in instance 1
|
||||||
watch_for(
|
watch_for(
|
||||||
browser: browser2,
|
browser: browser2,
|
||||||
|
@ -126,6 +169,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
no_quote: true,
|
no_quote: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
# check content in instance 2
|
# check content in instance 2
|
||||||
watch_for(
|
watch_for(
|
||||||
browser: browser2,
|
browser: browser2,
|
||||||
|
@ -160,6 +214,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
no_quote: true,
|
no_quote: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
# reload instances, verify again
|
# reload instances, verify again
|
||||||
reload(
|
reload(
|
||||||
browser: browser1,
|
browser: browser1,
|
||||||
|
@ -195,6 +260,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
no_quote: true,
|
no_quote: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
# change form of ticket, reset, reload and verify in instance 2
|
# change form of ticket, reset, reload and verify in instance 2
|
||||||
ticket_update(
|
ticket_update(
|
||||||
browser: browser2,
|
browser: browser2,
|
||||||
|
@ -244,6 +320,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
)
|
)
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
reload(
|
reload(
|
||||||
browser: browser2,
|
browser: browser2,
|
||||||
)
|
)
|
||||||
|
@ -260,6 +347,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
no_quote: true,
|
no_quote: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
task_type(
|
task_type(
|
||||||
browser: browser2,
|
browser: browser2,
|
||||||
type: 'stayOnTab',
|
type: 'stayOnTab',
|
||||||
|
@ -278,6 +376,17 @@ class AgentTicketUpdate2Test < TestCase
|
||||||
no_quote: true,
|
no_quote: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
browser: browser1,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'AT', # agent1
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
browser: browser2,
|
||||||
|
css: '.content.active .js-attributeBar .js-avatar .avatar--not-changed',
|
||||||
|
value: 'TA', # master
|
||||||
|
)
|
||||||
|
|
||||||
# check if new article is empty
|
# check if new article is empty
|
||||||
ticket_verify(
|
ticket_verify(
|
||||||
browser: browser2,
|
browser: browser2,
|
||||||
|
|
Loading…
Reference in a new issue