Added support to search for chat sessions. Added set name and tags for chats.
This commit is contained in:
parent
69ccf09906
commit
9c54b3382d
13 changed files with 417 additions and 87 deletions
|
@ -35,7 +35,7 @@ class App.CustomerChat extends App.Controller
|
||||||
active_agent_ids: []
|
active_agent_ids: []
|
||||||
|
|
||||||
@render()
|
@render()
|
||||||
@on 'layout-has-changed', @propagateLayoutChange
|
@on('layout-has-changed', @propagateLayoutChange)
|
||||||
|
|
||||||
# update navbar on new status
|
# update navbar on new status
|
||||||
@bind('chat_status_agent', (data) =>
|
@bind('chat_status_agent', (data) =>
|
||||||
|
@ -163,6 +163,11 @@ class App.CustomerChat extends App.Controller
|
||||||
@title 'Customer Chat', true
|
@title 'Customer Chat', true
|
||||||
@navupdate '#customer_chat'
|
@navupdate '#customer_chat'
|
||||||
|
|
||||||
|
if params.session_id && App.ChatSession.exists(params.session_id)
|
||||||
|
session = App.ChatSession.find(params.session_id)
|
||||||
|
@addChat(session)
|
||||||
|
@navigate '#customer_chat'
|
||||||
|
|
||||||
active: (state) =>
|
active: (state) =>
|
||||||
return @shown if state is undefined
|
return @shown if state is undefined
|
||||||
@shown = state
|
@shown = state
|
||||||
|
@ -264,10 +269,11 @@ class App.CustomerChat extends App.Controller
|
||||||
|
|
||||||
addChat: (session) ->
|
addChat: (session) ->
|
||||||
return if @chatWindows[session.session_id]
|
return if @chatWindows[session.session_id]
|
||||||
chat = new ChatWindow
|
chat = new ChatWindow(
|
||||||
session: session
|
session: session
|
||||||
removeCallback: @removeChat
|
removeCallback: @removeChat
|
||||||
messageCallback: @updateNavMenu
|
messageCallback: @updateNavMenu
|
||||||
|
)
|
||||||
|
|
||||||
@workspace.append chat.el
|
@workspace.append chat.el
|
||||||
chat.render()
|
chat.render()
|
||||||
|
@ -289,7 +295,7 @@ class App.CustomerChat extends App.Controller
|
||||||
propagateLayoutChange: (event) =>
|
propagateLayoutChange: (event) =>
|
||||||
# adjust scroll position on layoutChange
|
# adjust scroll position on layoutChange
|
||||||
for session_id, chat of @chatWindows
|
for session_id, chat of @chatWindows
|
||||||
chat.trigger 'layout-changed'
|
chat.trigger('layout-changed')
|
||||||
|
|
||||||
acceptChat: =>
|
acceptChat: =>
|
||||||
return if @windowCount() >= @maxChatWindows
|
return if @windowCount() >= @maxChatWindows
|
||||||
|
@ -324,19 +330,6 @@ class App.CustomerChat extends App.Controller
|
||||||
currentPosition: =>
|
currentPosition: =>
|
||||||
@$('.main').scrollTop()
|
@$('.main').scrollTop()
|
||||||
|
|
||||||
class CustomerChatRouter extends App.ControllerPermanent
|
|
||||||
requiredPermission: 'chat.agent'
|
|
||||||
constructor: (params) ->
|
|
||||||
super
|
|
||||||
|
|
||||||
App.TaskManager.execute(
|
|
||||||
key: 'CustomerChat'
|
|
||||||
controller: 'CustomerChat'
|
|
||||||
params: {}
|
|
||||||
show: true
|
|
||||||
persistent: true
|
|
||||||
)
|
|
||||||
|
|
||||||
class ChatWindow extends App.Controller
|
class ChatWindow extends App.Controller
|
||||||
className: 'chat-window'
|
className: 'chat-window'
|
||||||
|
|
||||||
|
@ -348,6 +341,8 @@ class ChatWindow extends App.Controller
|
||||||
'click .js-close': 'close'
|
'click .js-close': 'close'
|
||||||
'click .js-disconnect': 'disconnect'
|
'click .js-disconnect': 'disconnect'
|
||||||
'click .js-scrollHint': 'onScrollHintClick'
|
'click .js-scrollHint': 'onScrollHintClick'
|
||||||
|
'click .js-info': 'toggleMeta'
|
||||||
|
'submit .js-metaForm': 'sendMetaForm'
|
||||||
|
|
||||||
elements:
|
elements:
|
||||||
'.js-customerChatInput': 'input'
|
'.js-customerChatInput': 'input'
|
||||||
|
@ -355,8 +350,11 @@ class ChatWindow extends App.Controller
|
||||||
'.js-close': 'closeButton'
|
'.js-close': 'closeButton'
|
||||||
'.js-disconnect': 'disconnectButton'
|
'.js-disconnect': 'disconnectButton'
|
||||||
'.js-body': 'body'
|
'.js-body': 'body'
|
||||||
|
'.js-meta': 'meta'
|
||||||
|
'.js-name': 'metaName'
|
||||||
'.js-scrollHolder': 'scrollHolder'
|
'.js-scrollHolder': 'scrollHolder'
|
||||||
'.js-scrollHint': 'scrollHint'
|
'.js-scrollHint': 'scrollHint'
|
||||||
|
'.js-metaForm': 'metaForm'
|
||||||
|
|
||||||
sounds:
|
sounds:
|
||||||
message: new Audio('assets/sounds/chat_message.mp3')
|
message: new Audio('assets/sounds/chat_message.mp3')
|
||||||
|
@ -374,9 +372,11 @@ class ChatWindow extends App.Controller
|
||||||
@scrollSnapTolerance = 10 # pixels
|
@scrollSnapTolerance = 10 # pixels
|
||||||
|
|
||||||
@chat = App.Chat.find(@session.chat_id)
|
@chat = App.Chat.find(@session.chat_id)
|
||||||
@name = "#{@chat.displayName()} ##{@session.id}"
|
@name = @chat.displayName()
|
||||||
|
if @session && !_.isEmpty(@session.name)
|
||||||
|
@name = @session.name
|
||||||
|
|
||||||
@on 'layout-change', @onLayoutChange
|
@on('layout-change', @onLayoutChange)
|
||||||
|
|
||||||
@bind('chat_session_typing', (data) =>
|
@bind('chat_session_typing', (data) =>
|
||||||
return if data.session_id isnt @session.session_id
|
return if data.session_id isnt @session.session_id
|
||||||
|
@ -413,12 +413,44 @@ class ChatWindow extends App.Controller
|
||||||
onLayoutChange: =>
|
onLayoutChange: =>
|
||||||
@scrollToBottom()
|
@scrollToBottom()
|
||||||
|
|
||||||
render: ->
|
toggleMeta: =>
|
||||||
@html App.view('customer_chat/chat_window')
|
if @meta.hasClass('hidden')
|
||||||
name: @name
|
@showMeta()
|
||||||
|
else
|
||||||
|
@hideMeta()
|
||||||
|
|
||||||
@el.one 'transitionend', @onTransitionend
|
hideMeta: =>
|
||||||
@scrollHolder.scroll @detectScrolledtoBottom
|
@body.removeClass('hidden')
|
||||||
|
@meta.addClass('hidden')
|
||||||
|
@sendMetaForm()
|
||||||
|
|
||||||
|
showMeta: =>
|
||||||
|
@body.addClass('hidden')
|
||||||
|
@meta.removeClass('hidden')
|
||||||
|
|
||||||
|
sendMetaForm: (e) =>
|
||||||
|
if e
|
||||||
|
e.preventDefault()
|
||||||
|
params = @formParam(@metaForm)
|
||||||
|
|
||||||
|
App.WebSocket.send(
|
||||||
|
event:'chat_session_update'
|
||||||
|
data:
|
||||||
|
session_id: @session.session_id
|
||||||
|
name: params.name
|
||||||
|
tags: params.tags
|
||||||
|
)
|
||||||
|
|
||||||
|
@metaName.text(params.name)
|
||||||
|
|
||||||
|
render: ->
|
||||||
|
@html App.view('customer_chat/chat_window')(
|
||||||
|
name: @name
|
||||||
|
session: @session
|
||||||
|
)
|
||||||
|
|
||||||
|
@el.one('transitionend', @onTransitionend)
|
||||||
|
@scrollHolder.scroll(@detectScrolledtoBottom)
|
||||||
|
|
||||||
# force repaint
|
# force repaint
|
||||||
@el.prop('offsetHeight')
|
@el.prop('offsetHeight')
|
||||||
|
@ -426,18 +458,24 @@ class ChatWindow extends App.Controller
|
||||||
|
|
||||||
# @addMessage 'Hello. My name is Roger, how can I help you?', 'agent'
|
# @addMessage 'Hello. My name is Roger, how can I help you?', 'agent'
|
||||||
if @session
|
if @session
|
||||||
|
|
||||||
|
# set chat to offline if state is already closed
|
||||||
|
activeChat = true
|
||||||
|
if @session.state is 'closed'
|
||||||
|
activeChat = false
|
||||||
|
|
||||||
if @session && @session.preferences && @session.preferences.url
|
if @session && @session.preferences && @session.preferences.url
|
||||||
@addNoticeMessage(@session.preferences.url)
|
@addNoticeMessage(@session.preferences.url, undefined, activeChat)
|
||||||
|
|
||||||
if @session.messages
|
if @session.messages
|
||||||
for message in @session.messages
|
for message in @session.messages
|
||||||
if message.created_by_id
|
if message.created_by_id
|
||||||
@addMessage message.content, 'agent'
|
@addMessage(message.content, 'agent', false, activeChat)
|
||||||
else
|
else
|
||||||
@addMessage message.content, 'customer'
|
@addMessage(message.content, 'customer', false, activeChat)
|
||||||
|
|
||||||
# send init reply
|
# send init reply
|
||||||
if !@session.messages || _.isEmpty(@session.messages)
|
if activeChat && _.isEmpty(@session.messages)
|
||||||
preferences = @Session.get('preferences')
|
preferences = @Session.get('preferences')
|
||||||
if preferences.chat && preferences.chat.phrase
|
if preferences.chat && preferences.chat.phrase
|
||||||
phrases = preferences.chat.phrase[@session.chat_id]
|
phrases = preferences.chat.phrase[@session.chat_id]
|
||||||
|
@ -447,20 +485,9 @@ class ChatWindow extends App.Controller
|
||||||
@input.html(phrase)
|
@input.html(phrase)
|
||||||
@sendMessage(1600)
|
@sendMessage(1600)
|
||||||
|
|
||||||
@$('.js-info').popover(
|
# set chat to offline if state is already closed
|
||||||
trigger: 'hover'
|
if !activeChat
|
||||||
html: true
|
@goOffline()
|
||||||
animation: false
|
|
||||||
delay: 0
|
|
||||||
placement: 'bottom'
|
|
||||||
container: 'body' # place in body do prevent it from animating
|
|
||||||
title: ->
|
|
||||||
App.i18n.translateContent('Details')
|
|
||||||
content: =>
|
|
||||||
App.view('customer_chat/chat_window_info')(
|
|
||||||
session: @session
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# show text module UI
|
# show text module UI
|
||||||
new App.WidgetTextModule(
|
new App.WidgetTextModule(
|
||||||
|
@ -470,6 +497,18 @@ class ChatWindow extends App.Controller
|
||||||
config: App.Config.all()
|
config: App.Config.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
configureAttributesOutbound = [
|
||||||
|
{ name: 'name', display: 'Name', tag: 'input', null: true, },
|
||||||
|
{ name: 'tags', display: 'Tags', tag: 'tag', null: true, },
|
||||||
|
]
|
||||||
|
new App.ControllerForm(
|
||||||
|
el: @$('.js-metaForm')
|
||||||
|
model:
|
||||||
|
configure_attributes: configureAttributesOutbound
|
||||||
|
className: ''
|
||||||
|
params: @session
|
||||||
|
)
|
||||||
|
|
||||||
focus: =>
|
focus: =>
|
||||||
@input.focus()
|
@input.focus()
|
||||||
|
|
||||||
|
@ -498,7 +537,8 @@ class ChatWindow extends App.Controller
|
||||||
@goOffline()
|
@goOffline()
|
||||||
|
|
||||||
close: =>
|
close: =>
|
||||||
@el.one 'transitionend', { callback: @release }, @onTransitionend
|
@sendMetaForm()
|
||||||
|
@el.one('transitionend', { callback: @release }, @onTransitionend)
|
||||||
@el.removeClass('is-open')
|
@el.removeClass('is-open')
|
||||||
if @removeCallback
|
if @removeCallback
|
||||||
@removeCallback(@session.session_id)
|
@removeCallback(@session.session_id)
|
||||||
|
@ -577,7 +617,8 @@ class ChatWindow extends App.Controller
|
||||||
)
|
)
|
||||||
@delay(send, delay)
|
@delay(send, delay)
|
||||||
|
|
||||||
@addMessage content, 'agent'
|
@hideMeta()
|
||||||
|
@addMessage(content, 'agent')
|
||||||
@input.html('')
|
@input.html('')
|
||||||
|
|
||||||
updateModified: (state) =>
|
updateModified: (state) =>
|
||||||
|
@ -614,18 +655,19 @@ class ChatWindow extends App.Controller
|
||||||
@messageCallback(@session.session_id)
|
@messageCallback(@session.session_id)
|
||||||
@unreadMessagesCounter = 0
|
@unreadMessagesCounter = 0
|
||||||
|
|
||||||
addMessage: (message, sender, isNew) =>
|
addMessage: (message, sender, isNew, useMaybeAddTimestamp = true) =>
|
||||||
@maybeAddTimestamp()
|
@maybeAddTimestamp() if useMaybeAddTimestamp
|
||||||
|
|
||||||
@lastAddedType = sender
|
@lastAddedType = sender
|
||||||
|
|
||||||
@body.append App.view('customer_chat/chat_message')
|
@body.append App.view('customer_chat/chat_message')(
|
||||||
message: message
|
message: message
|
||||||
sender: sender
|
sender: sender
|
||||||
isNew: isNew
|
isNew: isNew
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
|
)
|
||||||
|
|
||||||
@scrollToBottom showHint: true
|
@scrollToBottom(showHint: true)
|
||||||
|
|
||||||
showWritingLoader: =>
|
showWritingLoader: =>
|
||||||
if !@isTyping
|
if !@isTyping
|
||||||
|
@ -667,33 +709,37 @@ class ChatWindow extends App.Controller
|
||||||
@lastAddedType = 'timestamp'
|
@lastAddedType = 'timestamp'
|
||||||
|
|
||||||
addTimestamp: (label, time) =>
|
addTimestamp: (label, time) =>
|
||||||
@body.append App.view('customer_chat/chat_timestamp')
|
@body.append App.view('customer_chat/chat_timestamp')(
|
||||||
label: label
|
label: label
|
||||||
time: time
|
time: time
|
||||||
|
)
|
||||||
|
|
||||||
updateLastTimestamp: (label, time) ->
|
updateLastTimestamp: (label, time) ->
|
||||||
@body
|
@body
|
||||||
.find('.js-timestamp')
|
.find('.js-timestamp')
|
||||||
.last()
|
.last()
|
||||||
.replaceWith App.view('customer_chat/chat_timestamp')
|
.replaceWith App.view('customer_chat/chat_timestamp')(
|
||||||
label: label
|
label: label
|
||||||
time: time
|
time: time
|
||||||
|
)
|
||||||
|
|
||||||
addStatusMessage: (message, args) ->
|
addStatusMessage: (message, args, useMaybeAddTimestamp = true) ->
|
||||||
@maybeAddTimestamp()
|
@maybeAddTimestamp() if useMaybeAddTimestamp
|
||||||
|
|
||||||
@body.append App.view('customer_chat/chat_status_message')
|
@body.append App.view('customer_chat/chat_status_message')(
|
||||||
message: message
|
message: message
|
||||||
args: args
|
args: args
|
||||||
|
)
|
||||||
|
|
||||||
@scrollToBottom()
|
@scrollToBottom()
|
||||||
|
|
||||||
addNoticeMessage: (message, args) ->
|
addNoticeMessage: (message, args, useMaybeAddTimestamp = true) ->
|
||||||
@maybeAddTimestamp()
|
@maybeAddTimestamp() if useMaybeAddTimestamp
|
||||||
|
|
||||||
@body.append App.view('customer_chat/chat_notice_message')
|
@body.append App.view('customer_chat/chat_notice_message')(
|
||||||
message: message
|
message: message
|
||||||
args: args
|
args: args
|
||||||
|
)
|
||||||
|
|
||||||
@scrollToBottom()
|
@scrollToBottom()
|
||||||
|
|
||||||
|
@ -784,6 +830,24 @@ class Setting extends App.ControllerModal
|
||||||
msg: App.i18n.translateContent(data.message)
|
msg: App.i18n.translateContent(data.message)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class CustomerChatRouter extends App.ControllerPermanent
|
||||||
|
requiredPermission: 'chat.agent'
|
||||||
|
constructor: (params) ->
|
||||||
|
super
|
||||||
|
|
||||||
|
# cleanup params
|
||||||
|
clean_params =
|
||||||
|
session_id: params.session_id
|
||||||
|
|
||||||
|
App.TaskManager.execute(
|
||||||
|
key: 'CustomerChat'
|
||||||
|
controller: 'CustomerChat'
|
||||||
|
params: clean_params
|
||||||
|
show: true
|
||||||
|
persistent: true
|
||||||
|
)
|
||||||
|
|
||||||
App.Config.set('customer_chat', CustomerChatRouter, 'Routes')
|
App.Config.set('customer_chat', CustomerChatRouter, 'Routes')
|
||||||
|
App.Config.set('customer_chat/session/:session_id', CustomerChatRouter, 'Routes')
|
||||||
App.Config.set('CustomerChat', { controller: 'CustomerChat', permission: ['chat.agent'] }, 'permanentTask')
|
App.Config.set('CustomerChat', { controller: 'CustomerChat', permission: ['chat.agent'] }, 'permanentTask')
|
||||||
App.Config.set('CustomerChat', { prio: 1200, parent: '', name: 'Customer Chat', target: '#customer_chat', key: 'CustomerChat', shown: false, permission: ['chat.agent'], class: 'chat' }, 'NavBar')
|
App.Config.set('CustomerChat', { prio: 1200, parent: '', name: 'Customer Chat', target: '#customer_chat', key: 'CustomerChat', shown: false, permission: ['chat.agent'], class: 'chat' }, 'NavBar')
|
||||||
|
|
|
@ -79,6 +79,7 @@ class App.Search extends App.Controller
|
||||||
|
|
||||||
@tabs = []
|
@tabs = []
|
||||||
for model in App.Config.get('models_searchable')
|
for model in App.Config.get('models_searchable')
|
||||||
|
model = model.replace(/::/, '')
|
||||||
tab =
|
tab =
|
||||||
name: model
|
name: model
|
||||||
model: model
|
model: model
|
||||||
|
|
32
app/assets/javascripts/app/models/chat_sessions.coffee
Normal file
32
app/assets/javascripts/app/models/chat_sessions.coffee
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
class App.ChatSession extends App.Model
|
||||||
|
@configure 'ChatSession', 'name', 'note'
|
||||||
|
@extend Spine.Model.Ajax
|
||||||
|
@url: @apiPath + '/chat_sessions'
|
||||||
|
|
||||||
|
@configure_attributes = [
|
||||||
|
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, 'null': false }
|
||||||
|
{ name: 'state', display: 'State', readonly: 1 }
|
||||||
|
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 }
|
||||||
|
{ name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 }
|
||||||
|
{ name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 }
|
||||||
|
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }
|
||||||
|
]
|
||||||
|
|
||||||
|
@configure_overview = [
|
||||||
|
'name',
|
||||||
|
'state',
|
||||||
|
'created_at',
|
||||||
|
]
|
||||||
|
|
||||||
|
uiUrl: ->
|
||||||
|
"#customer_chat/session/#{@id}"
|
||||||
|
|
||||||
|
searchResultAttributes: ->
|
||||||
|
displayName = ''
|
||||||
|
if !_.isEmpty(@name)
|
||||||
|
displayName = @displayName()
|
||||||
|
display: "##{@id} #{displayName}"
|
||||||
|
id: @id
|
||||||
|
class: 'chat_session chat_session-popover'
|
||||||
|
url: @uiUrl()
|
||||||
|
icon: 'chat'
|
|
@ -7,9 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-name">
|
<div class="chat-name">
|
||||||
<%= @name %> <div class="status-badge js-info">
|
<span class="js-name js-info u-clickable"><%= @name %><span> #<%= @session.id %>
|
||||||
<div class="info-badge"><%- @Icon('info') %></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-disconnect js-disconnect">
|
<div class="chat-disconnect js-disconnect">
|
||||||
<div class="btn btn--action btn--small"><%- @T('disconnect') %></div>
|
<div class="btn btn--action btn--small"><%- @T('disconnect') %></div>
|
||||||
|
@ -24,6 +22,25 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-body-holder js-scrollHolder">
|
<div class="chat-body-holder js-scrollHolder">
|
||||||
<div class="chat-body js-body"></div>
|
<div class="chat-body js-body"></div>
|
||||||
|
<div class="chat-body js-meta hidden">
|
||||||
|
<% if @session: %>
|
||||||
|
<ul>
|
||||||
|
<li><%- @T('Created at') %>: <%- @Ttimestamp(@session.created_at) %></li>
|
||||||
|
<% if @session && @session.preferences: %>
|
||||||
|
<% if @session.preferences.geo_ip: %>
|
||||||
|
<li>GeoIP: <%= @session.preferences.geo_ip.country_name %> <%= @session.preferences.geo_ip.city_name %></li>
|
||||||
|
<% end %>
|
||||||
|
<% if @session.preferences.remote_ip: %>
|
||||||
|
<li>IP: <%= @session.preferences.remote_ip %></li>
|
||||||
|
<% end %>
|
||||||
|
<% if @session.preferences.dns_name: %>
|
||||||
|
<li>DNS: <%= @session.preferences.dns_name %></li>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
<form class="js-metaForm" style="max-width: 200px; width: 100%;"></form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-controls">
|
<div class="chat-controls">
|
||||||
<div class="chat-input">
|
<div class="chat-input">
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
<hr>
|
|
||||||
<ul>
|
|
||||||
<% if @session: %>
|
|
||||||
<li><%- @T('Created at') %>: <%- @Ttimestamp(@session.created_at) %>
|
|
||||||
<% end %>
|
|
||||||
<% if @session && @session.preferences: %>
|
|
||||||
<% if @session.preferences.geo_ip: %>
|
|
||||||
<li>GeoIP: <%= @session.preferences.geo_ip.country_name %> <%= @session.preferences.geo_ip.city_name %>
|
|
||||||
<% end %>
|
|
||||||
<% if @session.preferences.remote_ip: %>
|
|
||||||
<li>IP: <%= @session.preferences.remote_ip %>
|
|
||||||
<% end %>
|
|
||||||
<% if @session.preferences.dns_name: %>
|
|
||||||
<li>DNS: <%= @session.preferences.dns_name %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
|
@ -33,9 +33,10 @@ class SearchController < ApplicationController
|
||||||
objects_in_order = []
|
objects_in_order = []
|
||||||
objects_in_order_hash = {}
|
objects_in_order_hash = {}
|
||||||
objects.each do |object|
|
objects.each do |object|
|
||||||
preferences = object.constantize.search_preferences(current_user)
|
local_class = object.constantize
|
||||||
|
preferences = local_class.search_preferences(current_user)
|
||||||
next if !preferences
|
next if !preferences
|
||||||
objects_in_order_hash[preferences[:prio]] = object
|
objects_in_order_hash[preferences[:prio]] = local_class
|
||||||
end
|
end
|
||||||
objects_in_order_hash.keys.sort.reverse_each do |prio|
|
objects_in_order_hash.keys.sort.reverse_each do |prio|
|
||||||
objects_in_order.push objects_in_order_hash[prio]
|
objects_in_order.push objects_in_order_hash[prio]
|
||||||
|
@ -64,16 +65,18 @@ class SearchController < ApplicationController
|
||||||
items = SearchIndexBackend.search(query, limit, objects_with_direct_search_index)
|
items = SearchIndexBackend.search(query, limit, objects_with_direct_search_index)
|
||||||
items.each do |item|
|
items.each do |item|
|
||||||
require item[:type].to_filename
|
require item[:type].to_filename
|
||||||
record = Kernel.const_get(item[:type]).lookup(id: item[:id])
|
local_class = Kernel.const_get(item[:type])
|
||||||
|
record = local_class.lookup(id: item[:id])
|
||||||
next if !record
|
next if !record
|
||||||
assets = record.assets(assets)
|
assets = record.assets(assets)
|
||||||
|
item[:type] = local_class.to_app_model.to_s
|
||||||
result.push item
|
result.push item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# e. g. do ticket query by Ticket class to handle ticket permissions
|
# e. g. do ticket query by Ticket class to handle ticket permissions
|
||||||
objects_without_direct_search_index.each do |object|
|
objects_without_direct_search_index.each do |object|
|
||||||
object_result = search_generic_backend(object, query, limit, current_user, assets)
|
object_result = search_generic_backend(object.constantize, query, limit, current_user, assets)
|
||||||
if object_result.present?
|
if object_result.present?
|
||||||
result = result.concat(object_result)
|
result = result.concat(object_result)
|
||||||
end
|
end
|
||||||
|
@ -83,7 +86,7 @@ class SearchController < ApplicationController
|
||||||
result_in_order = []
|
result_in_order = []
|
||||||
objects_in_order.each do |object|
|
objects_in_order.each do |object|
|
||||||
result.each do |item|
|
result.each do |item|
|
||||||
next if item[:type] != object
|
next if item[:type] != object.to_app_model.to_s
|
||||||
item[:id] = item[:id].to_i
|
item[:id] = item[:id].to_i
|
||||||
result_in_order.push item
|
result_in_order.push item
|
||||||
end
|
end
|
||||||
|
@ -110,7 +113,7 @@ class SearchController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def search_generic_backend(object, query, limit, current_user, assets)
|
def search_generic_backend(object, query, limit, current_user, assets)
|
||||||
found_objects = object.constantize.search(
|
found_objects = object.search(
|
||||||
query: query,
|
query: query,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
current_user: current_user,
|
current_user: current_user,
|
||||||
|
@ -119,7 +122,7 @@ class SearchController < ApplicationController
|
||||||
found_objects.each do |found_object|
|
found_objects.each do |found_object|
|
||||||
item = {
|
item = {
|
||||||
id: found_object.id,
|
id: found_object.id,
|
||||||
type: found_object.class.to_s
|
type: found_object.class.to_app_model.to_s
|
||||||
}
|
}
|
||||||
result.push item
|
result.push item
|
||||||
assets = found_object.assets(assets)
|
assets = found_object.assets(assets)
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
class Chat::Session < ApplicationModel
|
class Chat::Session < ApplicationModel
|
||||||
|
include HasSearchIndexBackend
|
||||||
|
include HasTags
|
||||||
|
|
||||||
|
extend Chat::Session::Search
|
||||||
|
load 'chat/session/search_index.rb'
|
||||||
|
include Chat::Session::SearchIndex
|
||||||
|
load 'chat/session/assets.rb'
|
||||||
|
include Chat::Session::Assets
|
||||||
|
|
||||||
|
has_many :messages, class_name: 'Chat::Message', foreign_key: 'chat_session_id'
|
||||||
|
|
||||||
before_create :generate_session_id
|
before_create :generate_session_id
|
||||||
store :preferences
|
store :preferences
|
||||||
|
|
||||||
|
|
56
app/models/chat/session/assets.rb
Normal file
56
app/models/chat/session/assets.rb
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
module Chat::Session::Assets
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get all assets / related models for this chat
|
||||||
|
|
||||||
|
chat = Chat::Session.find(123)
|
||||||
|
result = Chat::Session.assets(assets_if_exists)
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
result = {
|
||||||
|
users: {
|
||||||
|
123: user_model_123,
|
||||||
|
1234: user_model_1234,
|
||||||
|
},
|
||||||
|
chat_sessions: [ chat_session_model1 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def assets(data)
|
||||||
|
|
||||||
|
app_model_chat_session = Chat::Session.to_app_model
|
||||||
|
app_model_chat = Chat.to_app_model
|
||||||
|
app_model_user = User.to_app_model
|
||||||
|
|
||||||
|
data[ app_model_chat_session ] ||= {}
|
||||||
|
|
||||||
|
if !data[ app_model_chat_session ][ id ]
|
||||||
|
data[ app_model_chat_session ][ id ] = attributes_with_association_ids
|
||||||
|
data[ app_model_chat_session ][ id ]['messages'] = []
|
||||||
|
messages.each do |message|
|
||||||
|
data[ app_model_chat_session ][ id ]['messages'].push message.attributes
|
||||||
|
end
|
||||||
|
data[ app_model_chat_session ][ id ]['tags'] = tag_list
|
||||||
|
end
|
||||||
|
|
||||||
|
if !data[ app_model_chat ] || !data[ app_model_chat ][ chat_id ]
|
||||||
|
chat = Chat.lookup(id: chat_id)
|
||||||
|
if chat
|
||||||
|
data = chat.assets(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
%w[created_by_id updated_by_id].each do |local_user_id|
|
||||||
|
next if !self[ local_user_id ]
|
||||||
|
next if data[ app_model_user ] && data[ app_model_user ][ self[ local_user_id ] ]
|
||||||
|
user = User.lookup(id: self[ local_user_id ])
|
||||||
|
next if !user
|
||||||
|
data = user.assets(data)
|
||||||
|
end
|
||||||
|
data
|
||||||
|
end
|
||||||
|
end
|
80
app/models/chat/session/search.rb
Normal file
80
app/models/chat/session/search.rb
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class Chat::Session
|
||||||
|
module Search
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
search organizations preferences
|
||||||
|
|
||||||
|
result = Chat::Session.search_preferences(user_model)
|
||||||
|
|
||||||
|
returns if user has permissions to search
|
||||||
|
|
||||||
|
result = {
|
||||||
|
prio: 1000,
|
||||||
|
direct_search_index: true
|
||||||
|
}
|
||||||
|
|
||||||
|
returns if user has no permissions to search
|
||||||
|
|
||||||
|
result = false
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def search_preferences(current_user)
|
||||||
|
return false if Setting.get('chat') != true || !current_user.permissions?('chat.agent')
|
||||||
|
{
|
||||||
|
prio: 900,
|
||||||
|
direct_search_index: true,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
search organizations
|
||||||
|
|
||||||
|
result = Chat::Session.search(
|
||||||
|
current_user: User.find(123),
|
||||||
|
query: 'search something',
|
||||||
|
limit: 15,
|
||||||
|
)
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
result = [organization_model1, organization_model2]
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def search(params)
|
||||||
|
|
||||||
|
# get params
|
||||||
|
query = params[:query]
|
||||||
|
limit = params[:limit] || 10
|
||||||
|
current_user = params[:current_user]
|
||||||
|
|
||||||
|
# enable search only for agents and admins
|
||||||
|
return [] if !search_preferences(current_user)
|
||||||
|
|
||||||
|
# try search index backend
|
||||||
|
if SearchIndexBackend.enabled?
|
||||||
|
items = SearchIndexBackend.search(query, limit, 'Chat::Session')
|
||||||
|
chat_sessions = []
|
||||||
|
items.each do |item|
|
||||||
|
chat_session = Chat::Session.lookup(id: item[:id])
|
||||||
|
next if !chat_session
|
||||||
|
chat_sessions.push chat_session
|
||||||
|
end
|
||||||
|
return chat_sessions
|
||||||
|
end
|
||||||
|
|
||||||
|
# fallback do sql query
|
||||||
|
# - stip out * we already search for *query* -
|
||||||
|
query.delete! '*'
|
||||||
|
chat_sessions = Chat::Session.where(
|
||||||
|
'name LIKE ?', "%#{query}%"
|
||||||
|
).order('name').limit(limit).to_a
|
||||||
|
chat_sessions
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
36
app/models/chat/session/search_index.rb
Normal file
36
app/models/chat/session/search_index.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
module Chat::Session::SearchIndex
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
lookup name of ref. objects
|
||||||
|
|
||||||
|
chat_session = Chat::Session.find(123)
|
||||||
|
result = chat_session.search_index_attribute_lookup
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
attributes # object with lookup data
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def search_index_attribute_lookup
|
||||||
|
attributes = super
|
||||||
|
return if !attributes
|
||||||
|
|
||||||
|
attributes[:tag] = tag_list
|
||||||
|
|
||||||
|
messages = Chat::Message.where(chat_session_id: id)
|
||||||
|
attributes['messages'] = []
|
||||||
|
messages.each do |message|
|
||||||
|
|
||||||
|
# lookup attributes of ref. objects (normally name and note)
|
||||||
|
message_attributes = message.search_index_attribute_lookup
|
||||||
|
|
||||||
|
attributes['messages'].push message_attributes
|
||||||
|
end
|
||||||
|
|
||||||
|
attributes
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -49,7 +49,7 @@ class Sessions::Event::Base
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def permission_check(key, event)
|
def current_user_id
|
||||||
if !@session
|
if !@session
|
||||||
error = {
|
error = {
|
||||||
event: "#{event}_error",
|
event: "#{event}_error",
|
||||||
|
@ -60,7 +60,7 @@ class Sessions::Event::Base
|
||||||
Sessions.send(@client_id, error)
|
Sessions.send(@client_id, error)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if !@session['id']
|
if @session['id'].blank?
|
||||||
error = {
|
error = {
|
||||||
event: "#{event}_error",
|
event: "#{event}_error",
|
||||||
data: {
|
data: {
|
||||||
|
@ -70,7 +70,13 @@ class Sessions::Event::Base
|
||||||
Sessions.send(@client_id, error)
|
Sessions.send(@client_id, error)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
user = User.lookup(id: @session['id'])
|
@session['id']
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_user
|
||||||
|
user_id = current_user_id
|
||||||
|
return if !user_id
|
||||||
|
user = User.find_by(id: user_id)
|
||||||
if !user
|
if !user
|
||||||
error = {
|
error = {
|
||||||
event: "#{event}_error",
|
event: "#{event}_error",
|
||||||
|
@ -81,6 +87,12 @@ class Sessions::Event::Base
|
||||||
Sessions.send(@client_id, error)
|
Sessions.send(@client_id, error)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
def permission_check(key, event)
|
||||||
|
user = current_user
|
||||||
|
return if !user
|
||||||
if !user.permissions?(key)
|
if !user.permissions?(key)
|
||||||
error = {
|
error = {
|
||||||
event: "#{event}_error",
|
event: "#{event}_error",
|
||||||
|
|
35
lib/sessions/event/chat_session_update.rb
Normal file
35
lib/sessions/event/chat_session_update.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
class Sessions::Event::ChatSessionUpdate < Sessions::Event::ChatBase
|
||||||
|
|
||||||
|
def run
|
||||||
|
return super if super
|
||||||
|
return if !check_chat_session_exists
|
||||||
|
return if !permission_check('chat.agent', 'chat')
|
||||||
|
chat_session = current_chat_session
|
||||||
|
|
||||||
|
if @payload['data']['name'] != chat_session.name
|
||||||
|
chat_session.name = @payload['data']['name']
|
||||||
|
chat_session.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
if @payload['data']['tags']
|
||||||
|
new_tags = @payload['data']['tags'].split(',')
|
||||||
|
|
||||||
|
new_tags.each(&:strip!)
|
||||||
|
|
||||||
|
tags = chat_session.tag_list
|
||||||
|
new_tags.each do |new_tag|
|
||||||
|
next if new_tag.blank?
|
||||||
|
next if tags.include?(new_tag)
|
||||||
|
chat_session.tag_add(new_tag, current_user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
tags.each do |tag|
|
||||||
|
next if new_tags.include?(tag)
|
||||||
|
chat_session.tag_remove(tag, current_user_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -243,8 +243,8 @@ class ModelTest < ActiveSupport::TestCase
|
||||||
assert(searchable.include?(Ticket))
|
assert(searchable.include?(Ticket))
|
||||||
assert(searchable.include?(User))
|
assert(searchable.include?(User))
|
||||||
assert(searchable.include?(Organization))
|
assert(searchable.include?(Organization))
|
||||||
assert_equal(3, searchable.count)
|
assert(searchable.include?(Chat::Session))
|
||||||
|
assert_equal(4, searchable.count)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue