Fixes #967 - Access to my own Tickets (where I'm customer of) in a Group im not Agent.
This commit is contained in:
parent
59c787bacb
commit
4a07c783a3
56 changed files with 1063 additions and 378 deletions
|
@ -212,4 +212,15 @@ class Index extends App.ControllerContent
|
||||||
@formEnable(@$('.js-submit'), 'button')
|
@formEnable(@$('.js-submit'), 'button')
|
||||||
|
|
||||||
App.Config.set('customer_ticket_new', Index, 'Routes')
|
App.Config.set('customer_ticket_new', Index, 'Routes')
|
||||||
App.Config.set('CustomerTicketNew', { prio: 8003, parent: '#new', name: 'New Ticket', translate: true, target: '#customer_ticket_new', permission: ['ticket.customer'], setting: ['customer_ticket_create'], divider: true }, 'NavBarRight')
|
App.Config.set('CustomerTicketNew', {
|
||||||
|
prio: 8003,
|
||||||
|
parent: '#new',
|
||||||
|
name: 'New Ticket',
|
||||||
|
translate: true,
|
||||||
|
target: '#customer_ticket_new',
|
||||||
|
permission: (navigation) ->
|
||||||
|
return false if navigation.permissionCheck('ticket.agent')
|
||||||
|
return navigation.permissionCheck('ticket.customer')
|
||||||
|
setting: ['customer_ticket_create'],
|
||||||
|
divider: true
|
||||||
|
}, 'NavBarRight')
|
||||||
|
|
|
@ -361,6 +361,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
|
|
||||||
filterNavbarPermissionOk: (item) ->
|
filterNavbarPermissionOk: (item) ->
|
||||||
return true unless item.permission
|
return true unless item.permission
|
||||||
|
return item.permission(@) if typeof item.permission is 'function'
|
||||||
|
|
||||||
return _.any item.permission, (permissionName) =>
|
return _.any item.permission, (permissionName) =>
|
||||||
return @permissionCheck(permissionName)
|
return @permissionCheck(permissionName)
|
||||||
|
|
|
@ -1049,7 +1049,7 @@ class Table extends App.Controller
|
||||||
ticketListShow.push App.Ticket.find(ticket.id)
|
ticketListShow.push App.Ticket.find(ticket.id)
|
||||||
|
|
||||||
# if customer and no ticket exists, show the following message only
|
# if customer and no ticket exists, show the following message only
|
||||||
if !ticketListShow[0] && @permissionCheck('ticket.customer')
|
if !ticketListShow[0] && !@permissionCheck('ticket.agent')
|
||||||
@html App.view('customer_not_ticket_exists')()
|
@html App.view('customer_not_ticket_exists')()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1057,13 +1057,14 @@ class Table extends App.Controller
|
||||||
@overview = App.Overview.find(overview.id)
|
@overview = App.Overview.find(overview.id)
|
||||||
|
|
||||||
# render init page
|
# render init page
|
||||||
checkbox = true
|
checkbox = false
|
||||||
edit = false
|
edit = false
|
||||||
if @permissionCheck('admin.overview')
|
if @permissionCheck('admin.overview')
|
||||||
edit = true
|
edit = true
|
||||||
if @permissionCheck('ticket.customer')
|
if @permissionCheck('ticket.agent')
|
||||||
checkbox = false
|
checkbox = true
|
||||||
edit = false
|
view_modes = []
|
||||||
|
if @permissionCheck('ticket.agent')
|
||||||
view_modes = [
|
view_modes = [
|
||||||
{
|
{
|
||||||
name: 'S'
|
name: 'S'
|
||||||
|
@ -1076,8 +1077,6 @@ class Table extends App.Controller
|
||||||
class: 'active' if @view_mode is 'm'
|
class: 'active' if @view_mode is 'm'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
if @permissionCheck('ticket.customer')
|
|
||||||
view_modes = []
|
|
||||||
html = App.view('agent_ticket_view/content')(
|
html = App.view('agent_ticket_view/content')(
|
||||||
overview: @overview
|
overview: @overview
|
||||||
view_modes: view_modes
|
view_modes: view_modes
|
||||||
|
|
|
@ -137,23 +137,20 @@ class App.TicketZoom extends App.Controller
|
||||||
)
|
)
|
||||||
|
|
||||||
load: (data, ignoreSame = false, local = false) =>
|
load: (data, ignoreSame = false, local = false) =>
|
||||||
|
|
||||||
# check if ticket has changed
|
|
||||||
newTicketRaw = data.assets.Ticket[@ticket_id]
|
newTicketRaw = data.assets.Ticket[@ticket_id]
|
||||||
#console.log(newTicketRaw.updated_at)
|
|
||||||
#console.log(@ticketUpdatedAtLastCall)
|
|
||||||
|
|
||||||
|
loadAssets = true
|
||||||
if @ticketUpdatedAtLastCall
|
if @ticketUpdatedAtLastCall
|
||||||
|
|
||||||
# ignore if record is already shown
|
# ignore if record is already shown
|
||||||
if ignoreSame && new Date(newTicketRaw.updated_at).getTime() is new Date(@ticketUpdatedAtLastCall).getTime()
|
if ignoreSame && new Date(newTicketRaw.updated_at).getTime() is new Date(@ticketUpdatedAtLastCall).getTime()
|
||||||
#console.log('debug no fetched, current ticket already there or requested')
|
#console.log('debug no fetched, current ticket already there or requested')
|
||||||
return
|
loadAssets = false
|
||||||
|
|
||||||
# do not render if newer ticket is already requested
|
# do not render if newer ticket is already requested
|
||||||
if new Date(newTicketRaw.updated_at).getTime() < new Date(@ticketUpdatedAtLastCall).getTime()
|
if new Date(newTicketRaw.updated_at).getTime() < new Date(@ticketUpdatedAtLastCall).getTime()
|
||||||
#console.log('fetched no fetch, current ticket already newer')
|
#console.log('fetched no fetch, current ticket already newer')
|
||||||
return
|
loadAssets = false
|
||||||
|
|
||||||
# remember current record if newer as requested record
|
# remember current record if newer as requested record
|
||||||
if new Date(newTicketRaw.updated_at).getTime() > new Date(@ticketUpdatedAtLastCall).getTime()
|
if new Date(newTicketRaw.updated_at).getTime() > new Date(@ticketUpdatedAtLastCall).getTime()
|
||||||
|
@ -161,6 +158,9 @@ class App.TicketZoom extends App.Controller
|
||||||
else
|
else
|
||||||
@ticketUpdatedAtLastCall = newTicketRaw.updated_at
|
@ticketUpdatedAtLastCall = newTicketRaw.updated_at
|
||||||
|
|
||||||
|
# load assets
|
||||||
|
if loadAssets
|
||||||
|
|
||||||
# notify if ticket changed not by my self
|
# notify if ticket changed not by my self
|
||||||
if @initFetched
|
if @initFetched
|
||||||
if newTicketRaw.updated_by_id isnt @Session.get('id')
|
if newTicketRaw.updated_by_id isnt @Session.get('id')
|
||||||
|
@ -180,16 +180,31 @@ class App.TicketZoom extends App.Controller
|
||||||
# remember tags
|
# remember tags
|
||||||
@tags = data.tags
|
@tags = data.tags
|
||||||
|
|
||||||
# get edit form attributes
|
|
||||||
@formMeta = data.form_meta
|
|
||||||
|
|
||||||
# load assets
|
|
||||||
App.Collection.loadAssets(data.assets, targetModel: 'Ticket')
|
App.Collection.loadAssets(data.assets, targetModel: 'Ticket')
|
||||||
|
|
||||||
# get data
|
# get ticket
|
||||||
@ticket = App.Ticket.fullLocal(@ticket_id)
|
@ticket = App.Ticket.fullLocal(@ticket_id)
|
||||||
@ticket.article = undefined
|
@ticket.article = undefined
|
||||||
|
|
||||||
|
view = @ticket.currentView()
|
||||||
|
readable = @ticket.userGroupAccess('read')
|
||||||
|
changeable = @ticket.userGroupAccess('change')
|
||||||
|
fullable = @ticket.userGroupAccess('full')
|
||||||
|
formMeta = data.form_meta
|
||||||
|
|
||||||
|
# on the following states we want to rerender the ticket:
|
||||||
|
# - if the object attribute configuration has changed (attribute values, restrictions, filters)
|
||||||
|
# - if the user view has changed (agent/customer)
|
||||||
|
# - if the ticket permission has changed (read/write/full)
|
||||||
|
if @view && ( !_.isEqual(@formMeta, formMeta) || @view isnt view || @readable isnt readable || @changeable isnt changeable || @fullable isnt fullable )
|
||||||
|
@renderDone = false
|
||||||
|
|
||||||
|
@view = view
|
||||||
|
@readable = readable
|
||||||
|
@changeable = changeable
|
||||||
|
@fullable = fullable
|
||||||
|
@formMeta = formMeta
|
||||||
|
|
||||||
# render page
|
# render page
|
||||||
@render(local)
|
@render(local)
|
||||||
|
|
||||||
|
@ -410,7 +425,6 @@ class App.TicketZoom extends App.Controller
|
||||||
elLocal = $(App.view('ticket_zoom')
|
elLocal = $(App.view('ticket_zoom')
|
||||||
ticket: @ticket
|
ticket: @ticket
|
||||||
nav: @nav
|
nav: @nav
|
||||||
isCustomer: @permissionCheck('ticket.customer')
|
|
||||||
scrollbarWidth: App.Utils.getScrollBarWidth()
|
scrollbarWidth: App.Utils.getScrollBarWidth()
|
||||||
dir: App.i18n.dir()
|
dir: App.i18n.dir()
|
||||||
)
|
)
|
||||||
|
@ -460,6 +474,7 @@ class App.TicketZoom extends App.Controller
|
||||||
|
|
||||||
@highligher = new App.TicketZoomHighlighter(
|
@highligher = new App.TicketZoomHighlighter(
|
||||||
el: elLocal.find('.js-highlighterContainer')
|
el: elLocal.find('.js-highlighterContainer')
|
||||||
|
ticket: @ticket
|
||||||
ticket_id: @ticket_id
|
ticket_id: @ticket_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -611,12 +626,12 @@ class App.TicketZoom extends App.Controller
|
||||||
subject: ''
|
subject: ''
|
||||||
type: 'note'
|
type: 'note'
|
||||||
body: ''
|
body: ''
|
||||||
internal: internal
|
internal: ''
|
||||||
in_reply_to: ''
|
in_reply_to: ''
|
||||||
subtype: ''
|
subtype: ''
|
||||||
|
|
||||||
if @permissionCheck('ticket.customer')
|
if @ticket.currentView() is 'agent'
|
||||||
currentStore.article.internal = ''
|
currentStore.article.internal = internal
|
||||||
|
|
||||||
currentStore
|
currentStore
|
||||||
|
|
||||||
|
@ -637,7 +652,7 @@ class App.TicketZoom extends App.Controller
|
||||||
return if modelDiff.ticket.state_id
|
return if modelDiff.ticket.state_id
|
||||||
|
|
||||||
# and we are in the customer interface
|
# and we are in the customer interface
|
||||||
return if !@permissionCheck('ticket.customer')
|
return if @ticket.currentView() isnt 'customer'
|
||||||
|
|
||||||
# and the default is was not set before
|
# and the default is was not set before
|
||||||
return if @isDefaultFollowUpStateSet
|
return if @isDefaultFollowUpStateSet
|
||||||
|
@ -676,7 +691,7 @@ class App.TicketZoom extends App.Controller
|
||||||
|
|
||||||
delete currentParams.article.form_id
|
delete currentParams.article.form_id
|
||||||
|
|
||||||
if @permissionCheck('ticket.customer')
|
if @ticket.currentView() is 'customer'
|
||||||
currentParams.article.internal = ''
|
currentParams.article.internal = ''
|
||||||
|
|
||||||
currentParams
|
currentParams
|
||||||
|
@ -802,7 +817,7 @@ class App.TicketZoom extends App.Controller
|
||||||
)
|
)
|
||||||
|
|
||||||
# set defaults
|
# set defaults
|
||||||
if !@permissionCheck('ticket.customer')
|
if ticket.currentView() is 'agent'
|
||||||
if !ticket['owner_id']
|
if !ticket['owner_id']
|
||||||
ticket['owner_id'] = 1
|
ticket['owner_id'] = 1
|
||||||
|
|
||||||
|
@ -875,7 +890,7 @@ class App.TicketZoom extends App.Controller
|
||||||
return
|
return
|
||||||
|
|
||||||
# time tracking
|
# time tracking
|
||||||
if @permissionCheck('ticket.customer')
|
if ticket.currentView() is 'customer'
|
||||||
@submitPost(e, ticket, macro)
|
@submitPost(e, ticket, macro)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ class Delete
|
||||||
timeframe_miliseconds - (now - created_at)
|
timeframe_miliseconds - (now - created_at)
|
||||||
|
|
||||||
@deletableForAgent: (actions, ticket, article, ui) ->
|
@deletableForAgent: (actions, ticket, article, ui) ->
|
||||||
return false if !ui.permissionCheck('ticket.agent')
|
return false if ticket.currentView() is 'customer'
|
||||||
return false if article.created_by_id != App.User.current()?.id
|
return false if article.created_by_id != App.User.current()?.id
|
||||||
return false if article.type.communication
|
return false if article.type.communication
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class EmailReply extends App.Controller
|
class EmailReply extends App.Controller
|
||||||
@action: (actions, ticket, article, ui) ->
|
@action: (actions, ticket, article, ui) ->
|
||||||
return actions if !ui.permissionCheck('ticket.agent')
|
return actions if ticket.currentView() is 'customer'
|
||||||
group = ticket.group
|
group = ticket.group
|
||||||
return actions if !group.email_address_id
|
return actions if !group.email_address_id
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ class EmailReply extends App.Controller
|
||||||
true
|
true
|
||||||
|
|
||||||
@articleTypes: (articleTypes, ticket, ui) ->
|
@articleTypes: (articleTypes, ticket, ui) ->
|
||||||
return articleTypes if !ui.permissionCheck('ticket.agent')
|
return articleTypes if ticket.currentView() is 'customer'
|
||||||
group = ticket.group
|
group = ticket.group
|
||||||
return articleTypes if !group.email_address_id
|
return articleTypes if !group.email_address_id
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class FacebookReply
|
class FacebookReply
|
||||||
@action: (actions, ticket, article, ui) ->
|
@action: (actions, ticket, article, ui) ->
|
||||||
return actions if ui.permissionCheck('ticket.customer')
|
return actions if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
if article.type.name is 'facebook feed post' || article.type.name is 'facebook feed comment'
|
if article.type.name is 'facebook feed post' || article.type.name is 'facebook feed comment'
|
||||||
actions.push {
|
actions.push {
|
||||||
|
@ -35,7 +35,7 @@ class FacebookReply
|
||||||
true
|
true
|
||||||
|
|
||||||
@articleTypes: (articleTypes, ticket, ui) ->
|
@articleTypes: (articleTypes, ticket, ui) ->
|
||||||
return articleTypes if !ui.permissionCheck('ticket.agent')
|
return articleTypes if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
return articleTypes if !ticket || !ticket.create_article_type_id
|
return articleTypes if !ticket || !ticket.create_article_type_id
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class Internal
|
class Internal
|
||||||
@action: (actions, ticket, article, ui) ->
|
@action: (actions, ticket, article, ui) ->
|
||||||
return actions if ui.permissionCheck('ticket.customer')
|
return actions if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
if article.internal is true
|
if article.internal is true
|
||||||
actions.push {
|
actions.push {
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Note
|
||||||
|
|
||||||
@articleTypes: (articleTypes, ticket, ui) ->
|
@articleTypes: (articleTypes, ticket, ui) ->
|
||||||
internal = false
|
internal = false
|
||||||
if ui.permissionCheck('ticket.agent')
|
if ticket.currentView() is 'agent'
|
||||||
internal = ui.Config.get('ui_ticket_zoom_article_note_new_internal')
|
internal = ui.Config.get('ui_ticket_zoom_article_note_new_internal')
|
||||||
|
|
||||||
articleTypes.push {
|
articleTypes.push {
|
||||||
|
|
|
@ -6,7 +6,7 @@ class PhoneReply
|
||||||
true
|
true
|
||||||
|
|
||||||
@articleTypes: (articleTypes, ticket, ui) ->
|
@articleTypes: (articleTypes, ticket, ui) ->
|
||||||
return articleTypes if !ui.permissionCheck('ticket.agent')
|
return articleTypes if ticket.currentView() is 'customer'
|
||||||
articleTypes.push {
|
articleTypes.push {
|
||||||
name: 'phone'
|
name: 'phone'
|
||||||
icon: 'phone'
|
icon: 'phone'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class SmsReply
|
class SmsReply
|
||||||
@action: (actions, ticket, article, ui) ->
|
@action: (actions, ticket, article, ui) ->
|
||||||
return actions if ui.permissionCheck('ticket.customer')
|
return actions if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
if article.sender.name is 'Customer' && article.type.name is 'sms'
|
if article.sender.name is 'Customer' && article.type.name is 'sms'
|
||||||
actions.push {
|
actions.push {
|
||||||
|
@ -43,7 +43,7 @@ class SmsReply
|
||||||
true
|
true
|
||||||
|
|
||||||
@articleTypes: (articleTypes, ticket, ui) ->
|
@articleTypes: (articleTypes, ticket, ui) ->
|
||||||
return articleTypes if !ui.permissionCheck('ticket.agent')
|
return articleTypes if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
return articleTypes if !ticket || !ticket.create_article_type_id
|
return articleTypes if !ticket || !ticket.create_article_type_id
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class Split
|
class Split
|
||||||
@action: (actions, ticket, article, ui) ->
|
@action: (actions, ticket, article, ui) ->
|
||||||
return actions if ui.permissionCheck('ticket.customer')
|
return actions if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
actions.push {
|
actions.push {
|
||||||
name: 'split'
|
name: 'split'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class TelegramReply
|
class TelegramReply
|
||||||
@action: (actions, ticket, article, ui) ->
|
@action: (actions, ticket, article, ui) ->
|
||||||
return actions if ui.permissionCheck('ticket.customer')
|
return actions if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
if article.sender.name is 'Customer' && article.type.name is 'telegram personal-message'
|
if article.sender.name is 'Customer' && article.type.name is 'telegram personal-message'
|
||||||
actions.push {
|
actions.push {
|
||||||
|
@ -43,7 +43,7 @@ class TelegramReply
|
||||||
true
|
true
|
||||||
|
|
||||||
@articleTypes: (articleTypes, ticket, ui) ->
|
@articleTypes: (articleTypes, ticket, ui) ->
|
||||||
return articleTypes if !ui.permissionCheck('ticket.agent')
|
return articleTypes if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
return articleTypes if !ticket || !ticket.create_article_type_id
|
return articleTypes if !ticket || !ticket.create_article_type_id
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class TwitterReply
|
class TwitterReply
|
||||||
@action: (actions, ticket, article, ui) ->
|
@action: (actions, ticket, article, ui) ->
|
||||||
return actions if ui.permissionCheck('ticket.customer')
|
return actions if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
if article.type.name is 'twitter status'
|
if article.type.name is 'twitter status'
|
||||||
actions.push {
|
actions.push {
|
||||||
|
@ -126,7 +126,7 @@ class TwitterReply
|
||||||
})
|
})
|
||||||
|
|
||||||
@articleTypes: (articleTypes, ticket, ui) ->
|
@articleTypes: (articleTypes, ticket, ui) ->
|
||||||
return articleTypes if !ui.permissionCheck('ticket.agent')
|
return articleTypes if ticket.currentView() is 'customer'
|
||||||
|
|
||||||
return articleTypes if !ticket || !ticket.create_article_type_id
|
return articleTypes if !ticket || !ticket.create_article_type_id
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,12 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
|
||||||
@internalSelector = true
|
@internalSelector = false
|
||||||
@type = @defaults['type'] || 'note'
|
@type = @defaults['type'] || 'note'
|
||||||
@setPossibleArticleTypes()
|
@setPossibleArticleTypes()
|
||||||
|
|
||||||
if @permissionCheck('ticket.customer')
|
if @ticket.currentView() is 'agent'
|
||||||
@internalSelector = false
|
@internalSelector = true
|
||||||
|
|
||||||
@textareaHeight =
|
@textareaHeight =
|
||||||
open: 148
|
open: 148
|
||||||
|
@ -165,7 +165,7 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
articleTypes: @articleTypes
|
articleTypes: @articleTypes
|
||||||
article: @defaults
|
article: @defaults
|
||||||
form_id: @form_id
|
form_id: @form_id
|
||||||
isCustomer: @permissionCheck('ticket.customer')
|
isCustomer: ticket.currentView() is 'customer'
|
||||||
internalSelector: @internalSelector
|
internalSelector: @internalSelector
|
||||||
)
|
)
|
||||||
@setArticleTypePre(@type)
|
@setArticleTypePre(@type)
|
||||||
|
@ -246,7 +246,7 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
@bindAttachmentDelete()
|
@bindAttachmentDelete()
|
||||||
|
|
||||||
# show text module UI
|
# show text module UI
|
||||||
if !@permissionCheck('ticket.customer')
|
if ticket.currentView() is 'agent'
|
||||||
textModule = new App.WidgetTextModule(
|
textModule = new App.WidgetTextModule(
|
||||||
el: @$('.js-textarea').parent()
|
el: @$('.js-textarea').parent()
|
||||||
data:
|
data:
|
||||||
|
@ -272,16 +272,18 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
params.form_id = @form_id
|
params.form_id = @form_id
|
||||||
params.content_type = 'text/html'
|
params.content_type = 'text/html'
|
||||||
|
|
||||||
if @permissionCheck('ticket.customer')
|
ticket = App.Ticket.find(@ticket_id)
|
||||||
sender = App.TicketArticleSender.findByAttribute('name', 'Customer')
|
|
||||||
type = App.TicketArticleType.findByAttribute('name', 'web')
|
if ticket.currentView() is 'agent'
|
||||||
params.type_id = type.id
|
|
||||||
params.sender_id = sender.id
|
|
||||||
else
|
|
||||||
sender = App.TicketArticleSender.findByAttribute('name', 'Agent')
|
sender = App.TicketArticleSender.findByAttribute('name', 'Agent')
|
||||||
type = App.TicketArticleType.findByAttribute('name', params['type'])
|
type = App.TicketArticleType.findByAttribute('name', params['type'])
|
||||||
params.sender_id = sender.id
|
params.sender_id = sender.id
|
||||||
params.type_id = type.id
|
params.type_id = type.id
|
||||||
|
else
|
||||||
|
sender = App.TicketArticleSender.findByAttribute('name', 'Customer')
|
||||||
|
type = App.TicketArticleType.findByAttribute('name', 'web')
|
||||||
|
params.type_id = type.id
|
||||||
|
params.sender_id = sender.id
|
||||||
|
|
||||||
if params.internal
|
if params.internal
|
||||||
params.internal = true
|
params.internal = true
|
||||||
|
|
|
@ -46,7 +46,7 @@ class App.TicketZoomAttributeBar extends App.Controller
|
||||||
@macroLastUpdated = App.Macro.lastUpdatedAt()
|
@macroLastUpdated = App.Macro.lastUpdatedAt()
|
||||||
@possibleMacros = []
|
@possibleMacros = []
|
||||||
|
|
||||||
if _.isEmpty(macros) || !@permissionCheck('ticket.agent')
|
if _.isEmpty(macros) || @ticket.currentView() is 'customer'
|
||||||
macroDisabled = true
|
macroDisabled = true
|
||||||
else
|
else
|
||||||
for macro in macros
|
for macro in macros
|
||||||
|
@ -63,7 +63,7 @@ class App.TicketZoomAttributeBar extends App.Controller
|
||||||
))
|
))
|
||||||
@setSecondaryAction(@secondaryAction, localeEl)
|
@setSecondaryAction(@secondaryAction, localeEl)
|
||||||
|
|
||||||
if @permissionCheck('ticket.agent')
|
if @ticket.currentView() is 'agent'
|
||||||
@taskbarWatcher = new App.TaskbarWatcher(
|
@taskbarWatcher = new App.TaskbarWatcher(
|
||||||
taskKey: @taskKey
|
taskKey: @taskKey
|
||||||
el: localeEl.filter('.js-avatars')
|
el: localeEl.filter('.js-avatars')
|
||||||
|
|
|
@ -36,7 +36,7 @@ class App.TicketZoomHighlighter extends App.Controller
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
|
||||||
return if !@permissionCheck('ticket.agent')
|
return if @ticket.currentView() isnt 'agent'
|
||||||
|
|
||||||
@currentHighlights = {}
|
@currentHighlights = {}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ class App.TicketZoomHighlighter extends App.Controller
|
||||||
|
|
||||||
# for testing purposes the highlights get stored in article preferences
|
# for testing purposes the highlights get stored in article preferences
|
||||||
loadHighlights: (ticket_article_id) ->
|
loadHighlights: (ticket_article_id) ->
|
||||||
return if !@permissionCheck('ticket.agent')
|
return if @ticket.currentView() isnt 'agent'
|
||||||
article = App.TicketArticle.find(ticket_article_id)
|
article = App.TicketArticle.find(ticket_article_id)
|
||||||
return if !article.preferences
|
return if !article.preferences
|
||||||
return if !article.preferences.highlight
|
return if !article.preferences.highlight
|
||||||
|
|
|
@ -8,5 +8,5 @@ class App.TicketZoomMeta extends App.ObserverController
|
||||||
render: (ticket) =>
|
render: (ticket) =>
|
||||||
@html App.view('ticket_zoom/meta')(
|
@html App.view('ticket_zoom/meta')(
|
||||||
ticket: ticket
|
ticket: ticket
|
||||||
isCustomer: @permissionCheck('ticket.customer')
|
isCustomer: ticket.currentView() is 'customer'
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class SidebarCustomer extends App.Controller
|
class SidebarCustomer extends App.Controller
|
||||||
sidebarItem: =>
|
sidebarItem: =>
|
||||||
return if !@permissionCheck('ticket.agent')
|
return if @ticket.currentView() isnt 'agent'
|
||||||
@item = {
|
@item = {
|
||||||
name: 'customer'
|
name: 'customer'
|
||||||
badgeCallback: @badgeRender
|
badgeCallback: @badgeRender
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class SidebarOrganization extends App.Controller
|
class SidebarOrganization extends App.Controller
|
||||||
sidebarItem: =>
|
sidebarItem: =>
|
||||||
return if !@permissionCheck('ticket.agent')
|
return if @ticket.currentView() isnt 'agent'
|
||||||
return if !@ticket.organization_id
|
return if !@ticket.organization_id
|
||||||
@item = {
|
@item = {
|
||||||
name: 'organization'
|
name: 'organization'
|
||||||
|
|
|
@ -19,10 +19,10 @@ class Edit extends App.ObserverController
|
||||||
|
|
||||||
if followUpPossible == 'new_ticket' && ticketState != 'closed' ||
|
if followUpPossible == 'new_ticket' && ticketState != 'closed' ||
|
||||||
followUpPossible != 'new_ticket' ||
|
followUpPossible != 'new_ticket' ||
|
||||||
@permissionCheck('admin') || @permissionCheck('ticket.agent')
|
@permissionCheck('admin') || ticket.currentView() is 'agent'
|
||||||
new App.ControllerForm(
|
new App.ControllerForm(
|
||||||
elReplace: @el
|
elReplace: @el
|
||||||
model: App.Ticket
|
model: { configure_attributes: @formMeta.configure_attributes }
|
||||||
screen: 'edit'
|
screen: 'edit'
|
||||||
handlersConfig: handlers
|
handlersConfig: handlers
|
||||||
filter: @formMeta.filter
|
filter: @formMeta.filter
|
||||||
|
@ -35,7 +35,7 @@ class Edit extends App.ObserverController
|
||||||
else
|
else
|
||||||
new App.ControllerForm(
|
new App.ControllerForm(
|
||||||
elReplace: @el
|
elReplace: @el
|
||||||
model: App.Ticket
|
model: { configure_attributes: @formMeta.configure_attributes }
|
||||||
screen: 'edit'
|
screen: 'edit'
|
||||||
handlersConfig: handlers
|
handlersConfig: handlers
|
||||||
filter: @formMeta.filter
|
filter: @formMeta.filter
|
||||||
|
@ -76,7 +76,7 @@ class SidebarTicket extends App.Controller
|
||||||
sidebarHead: 'Ticket'
|
sidebarHead: 'Ticket'
|
||||||
sidebarCallback: @editTicket
|
sidebarCallback: @editTicket
|
||||||
}
|
}
|
||||||
if @permissionCheck('ticket.agent')
|
if @ticket.currentView() is 'agent'
|
||||||
@item.sidebarActions = [
|
@item.sidebarActions = [
|
||||||
{
|
{
|
||||||
title: 'History'
|
title: 'History'
|
||||||
|
@ -127,7 +127,7 @@ class SidebarTicket extends App.Controller
|
||||||
taskKey: @taskKey
|
taskKey: @taskKey
|
||||||
)
|
)
|
||||||
|
|
||||||
if @permissionCheck('ticket.agent')
|
if @ticket.currentView() is 'agent'
|
||||||
@tagWidget = new App.WidgetTag(
|
@tagWidget = new App.WidgetTag(
|
||||||
el: localEl.filter('.tags')
|
el: localEl.filter('.tags')
|
||||||
object_type: 'Ticket'
|
object_type: 'Ticket'
|
||||||
|
|
|
@ -4,7 +4,7 @@ class App.TicketZoomTimeUnit extends App.ObserverController
|
||||||
time_unit: true
|
time_unit: true
|
||||||
|
|
||||||
render: (ticket) =>
|
render: (ticket) =>
|
||||||
return if !@permissionCheck('ticket.agent')
|
return if ticket.currentView() isnt 'agent'
|
||||||
return if !ticket.time_unit
|
return if !ticket.time_unit
|
||||||
@html App.view('ticket_zoom/time_unit')(
|
@html App.view('ticket_zoom/time_unit')(
|
||||||
ticket: ticket
|
ticket: ticket
|
||||||
|
|
|
@ -305,12 +305,32 @@ class App.Ticket extends App.Model
|
||||||
editable: (permission = 'change') ->
|
editable: (permission = 'change') ->
|
||||||
user = App.User.current()
|
user = App.User.current()
|
||||||
return false if !user?
|
return false if !user?
|
||||||
return true if user.id is @customer_id
|
return true if @currentView() is 'customer' && @userIsCustomer()
|
||||||
return true if user.organization_id && @organization_id && user.organization_id is @organization_id
|
return true if @currentView() is 'customer' && user.organization_id && @organization_id && user.organization_id is @organization_id
|
||||||
return false if !@group_id
|
return @userGroupAccess(permission)
|
||||||
|
|
||||||
|
userGroupAccess: (permission) ->
|
||||||
|
user = App.User.current()
|
||||||
group_ids = user.allGroupIds(permission)
|
group_ids = user.allGroupIds(permission)
|
||||||
|
return false if !@group_id
|
||||||
|
|
||||||
for local_group_id in group_ids
|
for local_group_id in group_ids
|
||||||
if local_group_id.toString() is @group_id.toString()
|
if local_group_id.toString() is @group_id.toString()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
userIsCustomer: ->
|
||||||
|
user = App.User.current()
|
||||||
|
return true if user.id is @customer_id
|
||||||
false
|
false
|
||||||
|
|
||||||
|
userIsOwner: ->
|
||||||
|
user = App.User.current()
|
||||||
|
return true if user.id is @owner_id
|
||||||
|
false
|
||||||
|
|
||||||
|
currentView: ->
|
||||||
|
return 'agent' if App.User.current()?.permission('ticket.agent') && @userGroupAccess('read')
|
||||||
|
return 'customer' if App.User.current()?.permission('ticket.customer')
|
||||||
|
return
|
||||||
|
|
|
@ -39,6 +39,18 @@ module Channel::Filter::SenderIsSystemAddress
|
||||||
|
|
||||||
mail['x-zammad-ticket-create-article-sender'.to_sym] = 'Agent'
|
mail['x-zammad-ticket-create-article-sender'.to_sym] = 'Agent'
|
||||||
mail['x-zammad-article-sender'.to_sym] = 'Agent'
|
mail['x-zammad-article-sender'.to_sym] = 'Agent'
|
||||||
|
|
||||||
|
# if the agent is also customer of the ticket then
|
||||||
|
# we need to set the sender as customer.
|
||||||
|
ticket_id = mail['x-zammad-ticket-id'.to_sym]
|
||||||
|
if ticket_id.present?
|
||||||
|
ticket = Ticket.lookup(id: ticket_id)
|
||||||
|
|
||||||
|
if ticket.present? && ticket.customer_id == user.id
|
||||||
|
mail['x-zammad-ticket-create-article-sender'.to_sym] = 'Customer'
|
||||||
|
mail['x-zammad-article-sender'.to_sym] = 'Customer'
|
||||||
|
end
|
||||||
|
end
|
||||||
return true
|
return true
|
||||||
rescue => e
|
rescue => e
|
||||||
Rails.logger.error 'SenderIsSystemAddress: ' + e.inspect
|
Rails.logger.error 'SenderIsSystemAddress: ' + e.inspect
|
||||||
|
|
|
@ -455,116 +455,6 @@ get the attribute model based on object and name
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
get user based list of used object attributes
|
|
||||||
|
|
||||||
attribute_list = ObjectManager::Attribute.by_object('Ticket', user)
|
|
||||||
|
|
||||||
returns:
|
|
||||||
|
|
||||||
[
|
|
||||||
{ name: 'api_key', display: 'API KEY', tag: 'input', null: true, edit: true, maxlength: 32 },
|
|
||||||
{ name: 'api_ip_regexp', display: 'API IP RegExp', tag: 'input', null: true, edit: true },
|
|
||||||
{ name: 'api_ip_max', display: 'API IP Max', tag: 'input', null: true, edit: true },
|
|
||||||
]
|
|
||||||
|
|
||||||
=end
|
|
||||||
|
|
||||||
def self.by_object(object, user)
|
|
||||||
|
|
||||||
# lookups
|
|
||||||
if object
|
|
||||||
object_lookup_id = ObjectLookup.by_name(object)
|
|
||||||
end
|
|
||||||
|
|
||||||
# get attributes in right order
|
|
||||||
result = ObjectManager::Attribute.where(
|
|
||||||
object_lookup_id: object_lookup_id,
|
|
||||||
active: true,
|
|
||||||
to_create: false,
|
|
||||||
to_delete: false,
|
|
||||||
).order('position ASC, name ASC')
|
|
||||||
attributes = []
|
|
||||||
result.each do |item|
|
|
||||||
data = {
|
|
||||||
name: item.name,
|
|
||||||
display: item.display,
|
|
||||||
tag: item.data_type,
|
|
||||||
#:null => item.null,
|
|
||||||
}
|
|
||||||
if item.data_option[:permission]&.any?
|
|
||||||
next if !user
|
|
||||||
|
|
||||||
hint = false
|
|
||||||
item.data_option[:permission].each do |permission|
|
|
||||||
next if !user.permissions?(permission)
|
|
||||||
|
|
||||||
hint = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
next if !hint
|
|
||||||
end
|
|
||||||
|
|
||||||
if item.screens
|
|
||||||
data[:screen] = {}
|
|
||||||
item.screens.each do |screen, permission_options|
|
|
||||||
data[:screen][screen] = {}
|
|
||||||
|
|
||||||
if permission_options['-all-']
|
|
||||||
data[:screen][screen] = permission_options['-all-']
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
permission_options.each do |permission, options|
|
|
||||||
next if !user&.permissions?(permission)
|
|
||||||
|
|
||||||
options.each do |key, value|
|
|
||||||
if [true, false].include?(data[:screen][screen][key])
|
|
||||||
data[:screen][screen][key] = data[:screen][screen][key].nil? ? false : data[:screen][screen][key]
|
|
||||||
if options[key]
|
|
||||||
data[:screen][screen][key] = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
data[:screen][screen][key] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if item.data_option
|
|
||||||
data = data.merge(item.data_option.symbolize_keys)
|
|
||||||
end
|
|
||||||
attributes.push data
|
|
||||||
end
|
|
||||||
attributes
|
|
||||||
end
|
|
||||||
|
|
||||||
=begin
|
|
||||||
|
|
||||||
get user based list of object attributes as hash
|
|
||||||
|
|
||||||
attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
|
|
||||||
|
|
||||||
returns:
|
|
||||||
|
|
||||||
{
|
|
||||||
'api_key' => { name: 'api_key', display: 'API KEY', tag: 'input', null: true, edit: true, maxlength: 32 },
|
|
||||||
'api_ip_regexp' => { name: 'api_ip_regexp', display: 'API IP RegExp', tag: 'input', null: true, edit: true },
|
|
||||||
'api_ip_max' => { name: 'api_ip_max', display: 'API IP Max', tag: 'input', null: true, edit: true },
|
|
||||||
}
|
|
||||||
|
|
||||||
=end
|
|
||||||
|
|
||||||
def self.by_object_as_hash(object, user)
|
|
||||||
list = by_object(object, user)
|
|
||||||
hash = {}
|
|
||||||
list.each do |item|
|
|
||||||
hash[ item[:name] ] = item
|
|
||||||
end
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
|
|
||||||
=begin
|
|
||||||
|
|
||||||
discard migration changes
|
discard migration changes
|
||||||
|
|
||||||
ObjectManager::Attribute.discard_changes
|
ObjectManager::Attribute.discard_changes
|
||||||
|
|
7
app/models/object_manager/element.rb
Normal file
7
app/models/object_manager/element.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class ObjectManager::Element
|
||||||
|
include ::Mixin::HasBackends
|
||||||
|
|
||||||
|
def self.for_object(object)
|
||||||
|
"#{name}::#{object}".safe_constantize || ObjectManager::Element::Backend
|
||||||
|
end
|
||||||
|
end
|
68
app/models/object_manager/element/backend.rb
Normal file
68
app/models/object_manager/element/backend.rb
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
class ObjectManager::Element::Backend
|
||||||
|
|
||||||
|
attr_reader :user, :attribute, :record
|
||||||
|
|
||||||
|
def initialize(user:, attribute:, record:)
|
||||||
|
@user = user
|
||||||
|
@attribute = attribute
|
||||||
|
@record = record
|
||||||
|
end
|
||||||
|
|
||||||
|
def visible?
|
||||||
|
return true if attribute.data_option[:permission].blank?
|
||||||
|
return false if user.blank?
|
||||||
|
|
||||||
|
attribute.data_option[:permission].any? do |permission|
|
||||||
|
authorized?(permission)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized?(permission)
|
||||||
|
user.permissions?(permission)
|
||||||
|
end
|
||||||
|
|
||||||
|
def data
|
||||||
|
data = default_data
|
||||||
|
|
||||||
|
data[:screen] = screens if attribute.screens.present?
|
||||||
|
|
||||||
|
return data if attribute.data_option.blank?
|
||||||
|
|
||||||
|
data.merge(attribute.data_option.symbolize_keys)
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_data
|
||||||
|
{
|
||||||
|
name: attribute.name,
|
||||||
|
display: attribute.display,
|
||||||
|
tag: attribute.data_type,
|
||||||
|
#:null => attribute.null,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def screens
|
||||||
|
attribute.screens.transform_values do |permission_options|
|
||||||
|
screen_value(permission_options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def screen_value(permission_options)
|
||||||
|
return permission_options['-all-'] if permission_options['-all-']
|
||||||
|
return {} if user.blank?
|
||||||
|
|
||||||
|
screen_permission_options(permission_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def screen_permission_options(permission_options)
|
||||||
|
permission_options.each_with_object({}) do |(permission, options), result|
|
||||||
|
|
||||||
|
next if !authorized?(permission)
|
||||||
|
|
||||||
|
options.each do |key, value|
|
||||||
|
next if [true, false].include?(result[key]) && !value
|
||||||
|
|
||||||
|
result[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
37
app/models/object_manager/element/ticket.rb
Normal file
37
app/models/object_manager/element/ticket.rb
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
class ObjectManager::Element::Ticket < ObjectManager::Element::Backend
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def authorized?(permission)
|
||||||
|
return false if skip?(permission)
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def skip?(permission)
|
||||||
|
return true if agent_in_general_view?(permission)
|
||||||
|
return true if agent_access_missing?(permission)
|
||||||
|
|
||||||
|
authorized_customer_and_agent?(permission)
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent_in_general_view?(permission)
|
||||||
|
record.blank? && permission == 'ticket.customer' && agent?
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent_access_missing?(permission)
|
||||||
|
record.present? && permission == 'ticket.agent' && agent? && !read_access?
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized_customer_and_agent?(permission)
|
||||||
|
record.present? && permission == 'ticket.customer' && agent? && read_access?
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent?
|
||||||
|
user.permissions?('ticket.agent')
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_access?
|
||||||
|
user.group_access?(record.group_id, 'read')
|
||||||
|
end
|
||||||
|
end
|
62
app/models/object_manager/object.rb
Normal file
62
app/models/object_manager/object.rb
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
class ObjectManager::Object
|
||||||
|
attr_reader :object_name
|
||||||
|
|
||||||
|
def initialize(object_name)
|
||||||
|
@object_name = object_name
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get user based list of used object attributes
|
||||||
|
|
||||||
|
object = ObjectManager::Object.new('Ticket')
|
||||||
|
attribute_list = object.attributes(user)
|
||||||
|
|
||||||
|
returns:
|
||||||
|
|
||||||
|
[
|
||||||
|
{ name: 'api_key', display: 'API KEY', tag: 'input', null: true, edit: true, maxlength: 32 },
|
||||||
|
{ name: 'api_ip_regexp', display: 'API IP RegExp', tag: 'input', null: true, edit: true },
|
||||||
|
{ name: 'api_ip_max', display: 'API IP Max', tag: 'input', null: true, edit: true },
|
||||||
|
]
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def attributes(user, record = nil)
|
||||||
|
@attributes ||= begin
|
||||||
|
attribute_records.each_with_object([]) do |attribute_record, result|
|
||||||
|
|
||||||
|
element = element_class.new(
|
||||||
|
user: user,
|
||||||
|
attribute: attribute_record,
|
||||||
|
record: record,
|
||||||
|
)
|
||||||
|
|
||||||
|
next if !element.visible?
|
||||||
|
|
||||||
|
result.push element.data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def attribute_records
|
||||||
|
@attribute_records ||= begin
|
||||||
|
ObjectManager::Attribute.where(
|
||||||
|
object_lookup_id: object,
|
||||||
|
active: true,
|
||||||
|
to_create: false,
|
||||||
|
to_delete: false,
|
||||||
|
).order('position ASC, name ASC')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def object
|
||||||
|
@object ||= ObjectLookup.by_name(object_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def element_class
|
||||||
|
@element_class ||= ObjectManager::Element.for_object(object_name)
|
||||||
|
end
|
||||||
|
end
|
|
@ -90,13 +90,28 @@ returns
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.access_condition(user, access)
|
def self.access_condition(user, access)
|
||||||
|
sql = []
|
||||||
|
bind = []
|
||||||
|
|
||||||
if user.permissions?('ticket.agent')
|
if user.permissions?('ticket.agent')
|
||||||
['group_id IN (?)', user.group_ids_access(access)]
|
sql.push('group_id IN (?)')
|
||||||
elsif !user.organization || ( !user.organization.shared || user.organization.shared == false )
|
bind.push(user.group_ids_access(access))
|
||||||
['tickets.customer_id = ?', user.id]
|
|
||||||
else
|
|
||||||
['(tickets.customer_id = ? OR tickets.organization_id = ?)', user.id, user.organization.id]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if user.permissions?('ticket.customer')
|
||||||
|
if !user.organization || ( !user.organization.shared || user.organization.shared == false )
|
||||||
|
sql.push('tickets.customer_id = ?')
|
||||||
|
bind.push(user.id)
|
||||||
|
else
|
||||||
|
sql.push('(tickets.customer_id = ? OR tickets.organization_id = ?)')
|
||||||
|
bind.push(user.id)
|
||||||
|
bind.push(user.organization.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return if sql.blank?
|
||||||
|
|
||||||
|
[ sql.join(' OR ') ].concat(bind)
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
|
@ -19,33 +19,25 @@ returns
|
||||||
|
|
||||||
def self.all(data)
|
def self.all(data)
|
||||||
current_user = data[:current_user]
|
current_user = data[:current_user]
|
||||||
links = data[:links]
|
overview_filter = {}
|
||||||
|
|
||||||
# get customer overviews
|
|
||||||
role_ids = User.joins(:roles).where(users: { id: current_user.id, active: true }, roles: { active: true }).pluck('roles.id')
|
|
||||||
if current_user.permissions?('ticket.customer')
|
|
||||||
overview_filter = { active: true, organization_shared: false }
|
|
||||||
if current_user.organization_id && current_user.organization.shared
|
|
||||||
overview_filter.delete(:organization_shared)
|
|
||||||
end
|
|
||||||
if links.present?
|
|
||||||
overview_filter[:link] = links
|
|
||||||
end
|
|
||||||
overviews = Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: nil }, overviews: overview_filter).or(Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: current_user.id }, overviews: overview_filter)).distinct('overview.id').order(:prio, :name)
|
|
||||||
return overviews
|
|
||||||
end
|
|
||||||
|
|
||||||
# get agent overviews
|
|
||||||
return [] if !current_user.permissions?('ticket.agent')
|
|
||||||
|
|
||||||
overview_filter = { active: true }
|
|
||||||
overview_filter_not = { out_of_office: true }
|
overview_filter_not = { out_of_office: true }
|
||||||
|
|
||||||
|
return [] if !current_user.permissions?('ticket.customer') && !current_user.permissions?('ticket.agent')
|
||||||
|
|
||||||
|
role_ids = User.joins(:roles).where(users: { id: current_user.id, active: true }, roles: { active: true }).pluck('roles.id')
|
||||||
|
|
||||||
|
if data[:links].present?
|
||||||
|
overview_filter[:link] = data[:links]
|
||||||
|
end
|
||||||
|
|
||||||
|
overview_filter[:active] = true
|
||||||
if User.where('out_of_office = ? AND out_of_office_start_at <= ? AND out_of_office_end_at >= ? AND out_of_office_replacement_id = ? AND active = ?', true, Time.zone.today, Time.zone.today, current_user.id, true).count.positive?
|
if User.where('out_of_office = ? AND out_of_office_start_at <= ? AND out_of_office_end_at >= ? AND out_of_office_replacement_id = ? AND active = ?', true, Time.zone.today, Time.zone.today, current_user.id, true).count.positive?
|
||||||
overview_filter_not = {}
|
overview_filter_not = {}
|
||||||
end
|
end
|
||||||
if links.present?
|
if !current_user.organization_id || !current_user.organization.shared
|
||||||
overview_filter[:link] = links
|
overview_filter[:organization_shared] = false
|
||||||
end
|
end
|
||||||
|
|
||||||
Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: nil }, overviews: overview_filter).or(Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: current_user.id }, overviews: overview_filter)).where.not(overview_filter_not).distinct('overview.id').order(:prio, :name)
|
Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: nil }, overviews: overview_filter).or(Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: current_user.id }, overviews: overview_filter)).where.not(overview_filter_not).distinct('overview.id').order(:prio, :name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -131,26 +131,18 @@ returns
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
=begin
|
|
||||||
# for performance reasons we moved from api calls to optimized sql queries
|
|
||||||
groups.each do |group|
|
|
||||||
filter[:group_id].push group.id
|
|
||||||
assets = group.assets(assets)
|
|
||||||
dependencies[:group_id][group.id] = { owner_id: [] }
|
|
||||||
|
|
||||||
User.group_access(group.id, 'full').each do |user|
|
configure_attributes = nil
|
||||||
dependencies[:group_id][ group.id ][:owner_id].push user.id
|
if params[:ticket].present?
|
||||||
next if agents[user.id]
|
configure_attributes = ObjectManager::Object.new('Ticket').attributes(params[:current_user], params[:ticket])
|
||||||
agents[user.id] = true
|
|
||||||
assets = user.assets(assets)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
=end
|
|
||||||
{
|
{
|
||||||
assets: assets,
|
assets: assets,
|
||||||
form_meta: {
|
form_meta: {
|
||||||
filter: filter,
|
filter: filter,
|
||||||
dependencies: dependencies,
|
dependencies: dependencies,
|
||||||
|
configure_attributes: configure_attributes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -127,16 +127,18 @@ returns
|
||||||
|
|
||||||
# try search index backend
|
# try search index backend
|
||||||
if condition.blank? && SearchIndexBackend.enabled?
|
if condition.blank? && SearchIndexBackend.enabled?
|
||||||
query_extension = {}
|
|
||||||
query_extension['bool'] = {}
|
|
||||||
query_extension['bool']['must'] = []
|
|
||||||
|
|
||||||
|
query_or = []
|
||||||
if current_user.permissions?('ticket.agent')
|
if current_user.permissions?('ticket.agent')
|
||||||
group_ids = current_user.group_ids_access('read')
|
group_ids = current_user.group_ids_access('read')
|
||||||
|
if group_ids.present?
|
||||||
access_condition = {
|
access_condition = {
|
||||||
'query_string' => { 'default_field' => 'group_id', 'query' => "\"#{group_ids.join('" OR "')}\"" }
|
'query_string' => { 'default_field' => 'group_id', 'query' => "\"#{group_ids.join('" OR "')}\"" }
|
||||||
}
|
}
|
||||||
else
|
query_or.push(access_condition)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if current_user.permissions?('ticket.customer')
|
||||||
access_condition = if !current_user.organization || ( !current_user.organization.shared || current_user.organization.shared == false )
|
access_condition = if !current_user.organization || ( !current_user.organization.shared || current_user.organization.shared == false )
|
||||||
{
|
{
|
||||||
'query_string' => { 'default_field' => 'customer_id', 'query' => current_user.id }
|
'query_string' => { 'default_field' => 'customer_id', 'query' => current_user.id }
|
||||||
|
@ -150,9 +152,22 @@ returns
|
||||||
# customer_id: XXX OR organization_id: XXX
|
# customer_id: XXX OR organization_id: XXX
|
||||||
# conditions = [ '( customer_id = ? OR organization_id = ? )', current_user.id, current_user.organization.id ]
|
# conditions = [ '( customer_id = ? OR organization_id = ? )', current_user.id, current_user.organization.id ]
|
||||||
end
|
end
|
||||||
|
query_or.push(access_condition)
|
||||||
end
|
end
|
||||||
|
|
||||||
query_extension['bool']['must'].push access_condition
|
return [] if query_or.blank?
|
||||||
|
|
||||||
|
query_extension = {
|
||||||
|
'bool': {
|
||||||
|
'must': [
|
||||||
|
{
|
||||||
|
'bool': {
|
||||||
|
'should': query_or,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
items = SearchIndexBackend.search(query, 'Ticket', limit: limit,
|
items = SearchIndexBackend.search(query, 'Ticket', limit: limit,
|
||||||
query_extension: query_extension,
|
query_extension: query_extension,
|
||||||
|
|
|
@ -239,7 +239,7 @@ class Transaction::Notification
|
||||||
locale = user.locale
|
locale = user.locale
|
||||||
|
|
||||||
# only show allowed attributes
|
# only show allowed attributes
|
||||||
attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
|
attribute_list = ObjectManager::Object.new('Ticket').attributes(user).index_by { |item| item[:name] }
|
||||||
|
|
||||||
user_related_changes = {}
|
user_related_changes = {}
|
||||||
@item[:changes].each do |key, value|
|
@item[:changes].each do |key, value|
|
||||||
|
|
|
@ -208,7 +208,7 @@ class Transaction::Slack
|
||||||
locale = user.preferences[:locale] || Setting.get('locale_default') || 'en-us'
|
locale = user.preferences[:locale] || Setting.get('locale_default') || 'en-us'
|
||||||
|
|
||||||
# only show allowed attributes
|
# only show allowed attributes
|
||||||
attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
|
attribute_list = ObjectManager::Object.new('Ticket').attributes(user).index_by { |item| item[:name] }
|
||||||
#puts "AL #{attribute_list.inspect}"
|
#puts "AL #{attribute_list.inspect}"
|
||||||
user_related_changes = {}
|
user_related_changes = {}
|
||||||
@item[:changes].each do |key, value|
|
@item[:changes].each do |key, value|
|
||||||
|
|
|
@ -2,15 +2,15 @@ class OrganizationPolicy < ApplicationPolicy
|
||||||
|
|
||||||
def show?
|
def show?
|
||||||
return true if user.permissions?(['admin', 'ticket.agent'])
|
return true if user.permissions?(['admin', 'ticket.agent'])
|
||||||
return false if !user.permissions?('ticket.customer')
|
return true if record.id == user.organization_id
|
||||||
|
|
||||||
record.id == user.organization_id
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update?
|
def update?
|
||||||
return false if user.permissions?('ticket.customer')
|
return true if user.permissions?(['admin', 'ticket.agent'])
|
||||||
|
|
||||||
user.permissions?(['admin', 'ticket.agent'])
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
class Scope < ApplicationPolicy::Scope
|
class Scope < ApplicationPolicy::Scope
|
||||||
|
|
|
@ -55,9 +55,7 @@ class Ticket::ArticlePolicy < ApplicationPolicy
|
||||||
end
|
end
|
||||||
|
|
||||||
def access?(query)
|
def access?(query)
|
||||||
if record.internal == true && user.permissions?('ticket.customer')
|
return false if record.internal == true && !user.permissions?('ticket.agent')
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
ticket = Ticket.lookup(id: record.ticket_id)
|
ticket = Ticket.lookup(id: record.ticket_id)
|
||||||
Pundit.authorize(user, ticket, query)
|
Pundit.authorize(user, ticket, query)
|
||||||
|
|
|
@ -38,26 +38,26 @@ class TicketPolicy < ApplicationPolicy
|
||||||
|
|
||||||
def access?(access)
|
def access?(access)
|
||||||
|
|
||||||
|
# agent - access if requester is owner
|
||||||
|
return true if record.owner_id == user.id
|
||||||
|
|
||||||
|
# agent - access if requester is in group
|
||||||
|
return true if user.group_access?(record.group.id, access)
|
||||||
|
|
||||||
# check customer
|
# check customer
|
||||||
if user.permissions?('ticket.customer')
|
return false if !user.permissions?('ticket.customer')
|
||||||
|
|
||||||
# access ok if its own ticket
|
# access ok if its own ticket
|
||||||
return true if record.customer_id == user.id
|
return true if record.customer_id == user.id
|
||||||
|
|
||||||
# check organization ticket access
|
organization_access?
|
||||||
|
end
|
||||||
|
|
||||||
|
def organization_access?
|
||||||
return false if record.organization_id.blank?
|
return false if record.organization_id.blank?
|
||||||
return false if user.organization_id.blank?
|
return false if user.organization_id.blank?
|
||||||
return false if record.organization_id != user.organization_id
|
return false if record.organization_id != user.organization_id
|
||||||
|
|
||||||
return record.organization.shared?
|
record.organization.shared?
|
||||||
end
|
|
||||||
|
|
||||||
# check agent
|
|
||||||
|
|
||||||
# access if requester is owner
|
|
||||||
return true if record.owner_id == user.id
|
|
||||||
|
|
||||||
# access if requester is in group
|
|
||||||
user.group_access?(record.group.id, access)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
20
db/migrate/20200527000000_agent_customer.rb
Normal file
20
db/migrate/20200527000000_agent_customer.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
class AgentCustomer < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.exists?(name: 'system_init_done')
|
||||||
|
|
||||||
|
Role.where(name: %w[Admin Agent Customer]).each do |role|
|
||||||
|
role.preferences.delete(:not)
|
||||||
|
role.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
move_filter
|
||||||
|
end
|
||||||
|
|
||||||
|
def move_filter
|
||||||
|
Setting.find_by(name: '0010_postmaster_filter_trusted').update(name: '0005_postmaster_filter_trusted')
|
||||||
|
Setting.find_by(name: '0020_postmaster_filter_auto_response_check').update(name: '0006_postmaster_filter_auto_response_check')
|
||||||
|
Setting.find_by(name: '0100_postmaster_filter_follow_up_check').update(name: '0007_postmaster_filter_follow_up_check')
|
||||||
|
Setting.find_by(name: '0110_postmaster_filter_follow_up_merged').update(name: '0008_postmaster_filter_follow_up_merged')
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,9 +2,7 @@ Role.create_if_not_exists(
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Admin',
|
name: 'Admin',
|
||||||
note: 'To configure your system.',
|
note: 'To configure your system.',
|
||||||
preferences: {
|
preferences: {},
|
||||||
not: ['Customer'],
|
|
||||||
},
|
|
||||||
default_at_signup: false,
|
default_at_signup: false,
|
||||||
updated_by_id: 1,
|
updated_by_id: 1,
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
|
@ -14,9 +12,7 @@ Role.create_if_not_exists(
|
||||||
name: 'Agent',
|
name: 'Agent',
|
||||||
note: 'To work on Tickets.',
|
note: 'To work on Tickets.',
|
||||||
default_at_signup: false,
|
default_at_signup: false,
|
||||||
preferences: {
|
preferences: {},
|
||||||
not: ['Customer'],
|
|
||||||
},
|
|
||||||
updated_by_id: 1,
|
updated_by_id: 1,
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
)
|
)
|
||||||
|
@ -24,9 +20,7 @@ Role.create_if_not_exists(
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'Customer',
|
name: 'Customer',
|
||||||
note: 'People who create Tickets ask for help.',
|
note: 'People who create Tickets ask for help.',
|
||||||
preferences: {
|
preferences: {},
|
||||||
not: %w[Agent Admin],
|
|
||||||
},
|
|
||||||
default_at_signup: true,
|
default_at_signup: true,
|
||||||
updated_by_id: 1,
|
updated_by_id: 1,
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
|
|
|
@ -3306,13 +3306,40 @@ Setting.create_if_not_exists(
|
||||||
|
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Defines postmaster filter.',
|
title: 'Defines postmaster filter.',
|
||||||
name: '0010_postmaster_filter_trusted',
|
name: '0005_postmaster_filter_trusted',
|
||||||
area: 'Postmaster::PreFilter',
|
area: 'Postmaster::PreFilter',
|
||||||
description: 'Defines postmaster filter to remove X-Zammad headers from not trusted sources.',
|
description: 'Defines postmaster filter to remove X-Zammad headers from not trusted sources.',
|
||||||
options: {},
|
options: {},
|
||||||
state: 'Channel::Filter::Trusted',
|
state: 'Channel::Filter::Trusted',
|
||||||
frontend: false
|
frontend: false
|
||||||
)
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Defines postmaster filter.',
|
||||||
|
name: '0006_postmaster_filter_auto_response_check',
|
||||||
|
area: 'Postmaster::PreFilter',
|
||||||
|
description: 'Defines postmaster filter to identify auto responses to prevent auto replies from Zammad.',
|
||||||
|
options: {},
|
||||||
|
state: 'Channel::Filter::AutoResponseCheck',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Defines postmaster filter.',
|
||||||
|
name: '0007_postmaster_filter_follow_up_check',
|
||||||
|
area: 'Postmaster::PreFilter',
|
||||||
|
description: 'Defines postmaster filter to identify follow-ups (based on admin settings).',
|
||||||
|
options: {},
|
||||||
|
state: 'Channel::Filter::FollowUpCheck',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Defines postmaster filter.',
|
||||||
|
name: '0008_postmaster_filter_follow_up_merged',
|
||||||
|
area: 'Postmaster::PreFilter',
|
||||||
|
description: 'Defines postmaster filter to identify follow-up ticket for merged tickets.',
|
||||||
|
options: {},
|
||||||
|
state: 'Channel::Filter::FollowUpMerged',
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Defines postmaster filter.',
|
title: 'Defines postmaster filter.',
|
||||||
name: '0011_postmaster_sender_based_on_reply_to',
|
name: '0011_postmaster_sender_based_on_reply_to',
|
||||||
|
@ -3358,15 +3385,6 @@ Setting.create_if_not_exists(
|
||||||
state: 'Channel::Filter::SecureMailing',
|
state: 'Channel::Filter::SecureMailing',
|
||||||
frontend: false
|
frontend: false
|
||||||
)
|
)
|
||||||
Setting.create_if_not_exists(
|
|
||||||
title: 'Defines postmaster filter.',
|
|
||||||
name: '0020_postmaster_filter_auto_response_check',
|
|
||||||
area: 'Postmaster::PreFilter',
|
|
||||||
description: 'Defines postmaster filter to identify auto responses to prevent auto replies from Zammad.',
|
|
||||||
options: {},
|
|
||||||
state: 'Channel::Filter::AutoResponseCheck',
|
|
||||||
frontend: false
|
|
||||||
)
|
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Defines postmaster filter.',
|
title: 'Defines postmaster filter.',
|
||||||
name: '0030_postmaster_filter_out_of_office_check',
|
name: '0030_postmaster_filter_out_of_office_check',
|
||||||
|
@ -3376,24 +3394,6 @@ Setting.create_if_not_exists(
|
||||||
state: 'Channel::Filter::OutOfOfficeCheck',
|
state: 'Channel::Filter::OutOfOfficeCheck',
|
||||||
frontend: false
|
frontend: false
|
||||||
)
|
)
|
||||||
Setting.create_if_not_exists(
|
|
||||||
title: 'Defines postmaster filter.',
|
|
||||||
name: '0100_postmaster_filter_follow_up_check',
|
|
||||||
area: 'Postmaster::PreFilter',
|
|
||||||
description: 'Defines postmaster filter to identify follow-ups (based on admin settings).',
|
|
||||||
options: {},
|
|
||||||
state: 'Channel::Filter::FollowUpCheck',
|
|
||||||
frontend: false
|
|
||||||
)
|
|
||||||
Setting.create_if_not_exists(
|
|
||||||
title: 'Defines postmaster filter.',
|
|
||||||
name: '0110_postmaster_filter_follow_up_merged',
|
|
||||||
area: 'Postmaster::PreFilter',
|
|
||||||
description: 'Defines postmaster filter to identify follow-up ticket for merged tickets.',
|
|
||||||
options: {},
|
|
||||||
state: 'Channel::Filter::FollowUpMerged',
|
|
||||||
frontend: false
|
|
||||||
)
|
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Defines postmaster filter.',
|
title: 'Defines postmaster filter.',
|
||||||
name: '0200_postmaster_filter_follow_up_possible_check',
|
name: '0200_postmaster_filter_follow_up_possible_check',
|
||||||
|
|
|
@ -31,7 +31,7 @@ module SessionHelper
|
||||||
models = {}
|
models = {}
|
||||||
objects = ObjectManager.list_objects
|
objects = ObjectManager.list_objects
|
||||||
objects.each do |object|
|
objects.each do |object|
|
||||||
attributes = ObjectManager::Attribute.by_object(object, user)
|
attributes = ObjectManager::Object.new(object).attributes(user)
|
||||||
models[object] = attributes
|
models[object] = attributes
|
||||||
end
|
end
|
||||||
models
|
models
|
||||||
|
|
|
@ -65,6 +65,48 @@ window.onload = function() {
|
||||||
active: true,
|
active: true,
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
App.Role.refresh([
|
||||||
|
{
|
||||||
|
"name":"Agent",
|
||||||
|
"permission_ids":[
|
||||||
|
48,
|
||||||
|
],
|
||||||
|
"group_ids":{},
|
||||||
|
"default_at_signup":false,
|
||||||
|
"note":"To work on Tickets.",
|
||||||
|
"active":true,
|
||||||
|
"updated_at":"2020-07-29T14:57:27.304Z",
|
||||||
|
"id":2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Customer",
|
||||||
|
"permission_ids":[
|
||||||
|
49
|
||||||
|
],
|
||||||
|
"group_ids":{},
|
||||||
|
"default_at_signup":true,
|
||||||
|
"note":"People who create Tickets ask for help.",
|
||||||
|
"active":true,
|
||||||
|
"updated_at":"2020-07-29T14:57:27.314Z",
|
||||||
|
"id":3
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
App.Permission.refresh([
|
||||||
|
{
|
||||||
|
"name":"ticket.agent",
|
||||||
|
"note":"Access to Agent Tickets based on Group Access",
|
||||||
|
"active":true,
|
||||||
|
"id":48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ticket.customer",
|
||||||
|
"note":"Access to Customer Tickets based on current_user and organization",
|
||||||
|
"active":true,
|
||||||
|
"id":49
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
test('ticket.editabe customer user #1', function() {
|
test('ticket.editabe customer user #1', function() {
|
||||||
App.Session.set(33)
|
App.Session.set(33)
|
||||||
ticket1 = App.Ticket.find(1);
|
ticket1 = App.Ticket.find(1);
|
||||||
|
|
|
@ -3,7 +3,7 @@ FactoryBot.define do
|
||||||
sequence(:name) { |n| "Test Overview #{n}" }
|
sequence(:name) { |n| "Test Overview #{n}" }
|
||||||
prio { 1 }
|
prio { 1 }
|
||||||
role_ids { Role.where(name: %w[Customer Agent Admin]).pluck(:id) }
|
role_ids { Role.where(name: %w[Customer Agent Admin]).pluck(:id) }
|
||||||
out_of_office { true }
|
out_of_office { false }
|
||||||
updated_by_id { 1 }
|
updated_by_id { 1 }
|
||||||
created_by_id { 1 }
|
created_by_id { 1 }
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,14 @@ FactoryBot.define do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :agent_and_customer do
|
||||||
|
role_ids { Role.signup_role_ids.push( Role.find_by(name: 'Agent').id ).sort }
|
||||||
|
|
||||||
|
trait :with_org do
|
||||||
|
organization
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
factory :agent do
|
factory :agent do
|
||||||
roles { Role.where(name: 'Agent') }
|
roles { Role.where(name: 'Agent') }
|
||||||
end
|
end
|
||||||
|
|
|
@ -140,6 +140,24 @@ RSpec.describe Channel::EmailParser, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when from address matches an existing agent customer' do
|
||||||
|
let!(:agent_customer) { create(:agent_and_customer, email: 'foo@bar.com') }
|
||||||
|
let!(:ticket) { create(:ticket, customer: agent_customer) }
|
||||||
|
let!(:raw_email) { <<~RAW.chomp }
|
||||||
|
From: foo@bar.com
|
||||||
|
To: myzammad@example.com
|
||||||
|
Subject: [#{Setting.get('ticket_hook') + Setting.get('ticket_hook_divider') + ticket.number}] test
|
||||||
|
|
||||||
|
Lorem ipsum dolor
|
||||||
|
RAW
|
||||||
|
|
||||||
|
it 'sets article.sender to "Customer"' do
|
||||||
|
described_class.new.process({}, raw_email)
|
||||||
|
|
||||||
|
expect(Ticket::Article.last.sender.name).to eq('Customer')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when from address matches an existing customer' do
|
context 'when from address matches an existing customer' do
|
||||||
let!(:customer) { create(:customer, email: 'foo@bar.com') }
|
let!(:customer) { create(:customer, email: 'foo@bar.com') }
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,6 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe ObjectManager::Attribute, type: :model do
|
RSpec.describe ObjectManager::Attribute, type: :model do
|
||||||
|
|
||||||
let(:user_attribute_permissions) do
|
|
||||||
create(:user, roles: [role_attribute_permissions])
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:role_attribute_permissions) do
|
|
||||||
create(:role).tap do |role|
|
|
||||||
role.permission_grant('admin.organization')
|
|
||||||
role.permission_grant('ticket.agent')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'callbacks' do
|
describe 'callbacks' do
|
||||||
context 'for setting default values on local data options' do
|
context 'for setting default values on local data options' do
|
||||||
let(:subject) { described_class.new }
|
let(:subject) { described_class.new }
|
||||||
|
@ -118,36 +107,4 @@ RSpec.describe ObjectManager::Attribute, type: :model do
|
||||||
end.not_to raise_error
|
end.not_to raise_error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'attribute permissions', db_strategy: :reset do
|
|
||||||
it 'merges attribute permissions' do
|
|
||||||
create(:object_manager_attribute_text, screens: { create: { 'admin.organization': { shown: true }, 'ticket.agent': { shown: false } } }, name: 'test_permissions')
|
|
||||||
|
|
||||||
migration = described_class.migration_execute
|
|
||||||
expect(migration).to be true
|
|
||||||
|
|
||||||
attribute = described_class.by_object('Ticket', user_attribute_permissions).detect { |attr| attr[:name] == 'test_permissions' }
|
|
||||||
expect(attribute[:screen]['create']['shown']).to be true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'overwrites permissions if all get set' do
|
|
||||||
create(:object_manager_attribute_text, screens: { create: { '-all-': { shown: true }, 'admin.organization': { shown: false }, 'ticket.agent': { shown: false } } }, name: 'test_permissions_all')
|
|
||||||
|
|
||||||
migration = described_class.migration_execute
|
|
||||||
expect(migration).to be true
|
|
||||||
|
|
||||||
attribute = described_class.by_object('Ticket', user_attribute_permissions).detect { |attr| attr[:name] == 'test_permissions_all' }
|
|
||||||
expect(attribute[:screen]['create']['shown']).to be true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'is able to handle other values than true or false' do
|
|
||||||
create(:object_manager_attribute_text, screens: { create: { '-all-': { shown: true, item_class: 'column' }, 'admin.organization': { shown: false }, 'ticket.agent': { shown: false } } }, name: 'test_permissions_item')
|
|
||||||
|
|
||||||
migration = described_class.migration_execute
|
|
||||||
expect(migration).to be true
|
|
||||||
|
|
||||||
attribute = described_class.by_object('Ticket', user_attribute_permissions).detect { |attr| attr[:name] == 'test_permissions_item' }
|
|
||||||
expect(attribute[:screen]['create']['item_class']).to eq('column')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
110
spec/models/object_manager/object_spec.rb
Normal file
110
spec/models/object_manager/object_spec.rb
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe ObjectManager::Object do
|
||||||
|
|
||||||
|
describe 'attribute permissions', db_strategy: :reset do
|
||||||
|
|
||||||
|
let(:user) do
|
||||||
|
create(:user, roles: [role_attribute_permissions])
|
||||||
|
end
|
||||||
|
let(:attribute) { described_class.new('Ticket').attributes(user).detect { |attribute| attribute[:name] == attribute_name } }
|
||||||
|
|
||||||
|
let(:role_attribute_permissions) do
|
||||||
|
create(:role).tap do |role|
|
||||||
|
role.permission_grant('admin.organization')
|
||||||
|
role.permission_grant('ticket.agent')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:attribute_name) { 'example_attribute' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:object_manager_attribute_text, name: attribute_name, screens: screens)
|
||||||
|
ObjectManager::Attribute.migration_execute
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when true and false values for show exist' do
|
||||||
|
let(:screens) do
|
||||||
|
{
|
||||||
|
create: {
|
||||||
|
'admin.organization': {
|
||||||
|
shown: true
|
||||||
|
},
|
||||||
|
'ticket.agent': {
|
||||||
|
shown: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses true' do
|
||||||
|
expect(attribute[:screen]['create']['shown']).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when -all- is present' do
|
||||||
|
let(:screens) do
|
||||||
|
{
|
||||||
|
create: {
|
||||||
|
'-all-': {
|
||||||
|
shown: true
|
||||||
|
},
|
||||||
|
'admin.organization': {
|
||||||
|
shown: false
|
||||||
|
},
|
||||||
|
'ticket.agent': {
|
||||||
|
shown: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'takes its values into account' do
|
||||||
|
expect(attribute[:screen]['create']['shown']).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when non boolean values are present' do
|
||||||
|
let(:screens) do
|
||||||
|
{
|
||||||
|
create: {
|
||||||
|
'-all-': {
|
||||||
|
shown: true,
|
||||||
|
item_class: 'column'
|
||||||
|
},
|
||||||
|
'admin.organization': {
|
||||||
|
shown: false
|
||||||
|
},
|
||||||
|
'ticket.agent': {
|
||||||
|
shown: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'takes these values into account' do
|
||||||
|
expect(attribute[:screen]['create']['item_class']).to eq('column')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when agent is also customer' do
|
||||||
|
let(:user) { create(:agent_and_customer) }
|
||||||
|
let(:screens) do
|
||||||
|
{
|
||||||
|
create: {
|
||||||
|
'ticket.customer': {
|
||||||
|
filter: [2, 4]
|
||||||
|
},
|
||||||
|
'ticket.agent': {
|
||||||
|
filter: [3, 5]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prefers agent over customer permissions' do
|
||||||
|
expect(attribute[:screen]['create']['filter']).to eq([3, 5])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,7 +2,48 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Ticket::Overviews do
|
RSpec.describe Ticket::Overviews do
|
||||||
|
|
||||||
describe '#index' do
|
describe '.all' do
|
||||||
|
|
||||||
|
let(:views) { described_class.all(current_user: current_user).map(&:name) }
|
||||||
|
|
||||||
|
shared_examples 'containing' do |overview|
|
||||||
|
it "returns #{overview}" do
|
||||||
|
expect(views).to include(overview)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'not containing' do |overview|
|
||||||
|
it "doesn't return #{overview}" do
|
||||||
|
expect(views).not_to include(overview)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Agent' do
|
||||||
|
let(:current_user) { create(:agent) }
|
||||||
|
|
||||||
|
it_behaves_like 'containing', 'Open'
|
||||||
|
it_behaves_like 'not containing', 'My Tickets'
|
||||||
|
it_behaves_like 'not containing', 'My Organization Tickets'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Agent is also Customer' do
|
||||||
|
let(:current_user) { create(:agent_and_customer, :with_org) }
|
||||||
|
|
||||||
|
it_behaves_like 'containing', 'Open'
|
||||||
|
it_behaves_like 'containing', 'My Tickets'
|
||||||
|
it_behaves_like 'containing', 'My Organization Tickets'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Customer' do
|
||||||
|
let(:current_user) { create(:customer, :with_org) }
|
||||||
|
|
||||||
|
it_behaves_like 'not containing', 'Open'
|
||||||
|
it_behaves_like 'containing', 'My Tickets'
|
||||||
|
it_behaves_like 'containing', 'My Organization Tickets'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.index' do
|
||||||
|
|
||||||
# https://github.com/zammad/zammad/issues/1769
|
# https://github.com/zammad/zammad/issues/1769
|
||||||
it 'does not return multiple results for a single ticket' do
|
it 'does not return multiple results for a single ticket' do
|
||||||
|
|
|
@ -861,6 +861,123 @@ RSpec.describe Ticket, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.search' do
|
||||||
|
|
||||||
|
shared_examples 'search permissions' do
|
||||||
|
let(:group) { create(:group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'permitted' do
|
||||||
|
it 'finds Ticket' do
|
||||||
|
expect( described_class.search(query: ticket.number, current_user: current_user).count ).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'no permission' do
|
||||||
|
it "doesn't find Ticket" do
|
||||||
|
expect( described_class.search(query: ticket.number, current_user: current_user) ).to be_blank
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'Agent with Group access' do
|
||||||
|
|
||||||
|
let(:ticket) do
|
||||||
|
ticket = create(:ticket, group: group)
|
||||||
|
create(:ticket_article, ticket: ticket)
|
||||||
|
ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:current_user) { create(:agent, groups: [group]) }
|
||||||
|
|
||||||
|
it_behaves_like 'permitted'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Agent is Customer of Ticket' do
|
||||||
|
|
||||||
|
let(:ticket) do
|
||||||
|
ticket = create(:ticket, customer: current_user)
|
||||||
|
create(:ticket_article, ticket: ticket)
|
||||||
|
ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:current_user) { create(:agent_and_customer) }
|
||||||
|
|
||||||
|
it_behaves_like 'permitted'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for Organization access' do
|
||||||
|
|
||||||
|
let(:ticket) do
|
||||||
|
ticket = create(:ticket, customer: customer)
|
||||||
|
create(:ticket_article, ticket: ticket)
|
||||||
|
ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:customer) { create(:customer, organization: organization) }
|
||||||
|
|
||||||
|
context 'when Organization is shared' do
|
||||||
|
let(:organization) { create(:organization, shared: true) }
|
||||||
|
|
||||||
|
context 'for unrelated Agent' do
|
||||||
|
let(:current_user) { create(:agent) }
|
||||||
|
|
||||||
|
it_behaves_like 'no permission'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for Agent in same Organization' do
|
||||||
|
let(:current_user) { create(:agent_and_customer, organization: organization) }
|
||||||
|
|
||||||
|
it_behaves_like 'permitted'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for Customer of Ticket' do
|
||||||
|
let(:current_user) { customer }
|
||||||
|
|
||||||
|
it_behaves_like 'permitted'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Organization is not shared' do
|
||||||
|
let(:organization) { create(:organization, shared: false) }
|
||||||
|
|
||||||
|
context 'for unrelated Agent' do
|
||||||
|
let(:current_user) { create(:agent) }
|
||||||
|
|
||||||
|
it_behaves_like 'no permission'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for Agent in same Organization' do
|
||||||
|
let(:current_user) { create(:agent_and_customer, organization: organization) }
|
||||||
|
|
||||||
|
it_behaves_like 'no permission'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for Customer of Ticket' do
|
||||||
|
let(:current_user) { customer }
|
||||||
|
|
||||||
|
it_behaves_like 'permitted'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with searchindex', searchindex: true do
|
||||||
|
|
||||||
|
include_examples 'search permissions' do
|
||||||
|
before do
|
||||||
|
configure_elasticsearch(required: true, rebuild: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without searchindex' do
|
||||||
|
include_examples 'search permissions'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'Callbacks & Observers -' do
|
describe 'Callbacks & Observers -' do
|
||||||
describe 'NULL byte handling (via ChecksAttributeValuesAndLength concern):' do
|
describe 'NULL byte handling (via ChecksAttributeValuesAndLength concern):' do
|
||||||
it 'removes them from title on creation, if necessary (postgres doesn’t like them)' do
|
it 'removes them from title on creation, if necessary (postgres doesn’t like them)' do
|
||||||
|
|
38
spec/policies/organization_policy_spec.rb
Normal file
38
spec/policies/organization_policy_spec.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe OrganizationPolicy do
|
||||||
|
subject { described_class.new(user, record) }
|
||||||
|
|
||||||
|
let(:record) { create(:organization) }
|
||||||
|
|
||||||
|
context 'when customer' do
|
||||||
|
let(:user) { create(:customer, organization: record) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show]) }
|
||||||
|
it { is_expected.not_to permit_actions(%i[update]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when customer without organization' do
|
||||||
|
let(:user) { create(:customer) }
|
||||||
|
|
||||||
|
it { is_expected.not_to permit_actions(%i[show update]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when agent and customer' do
|
||||||
|
let(:user) { create(:agent_and_customer, organization: record) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show update]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when agent' do
|
||||||
|
let(:user) { create(:agent) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show update]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when admin' do
|
||||||
|
let(:user) { create(:admin) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show update]) }
|
||||||
|
end
|
||||||
|
end
|
57
spec/policies/ticket/article_policy_spec.rb
Normal file
57
spec/policies/ticket/article_policy_spec.rb
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Ticket::ArticlePolicy do
|
||||||
|
subject { described_class.new(user, record) }
|
||||||
|
|
||||||
|
let!(:group) { create(:group) }
|
||||||
|
let!(:ticket_customer) { create(:customer) }
|
||||||
|
|
||||||
|
let(:record) do
|
||||||
|
ticket = create(:ticket, group: group, customer: ticket_customer)
|
||||||
|
create(:ticket_article, ticket: ticket)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article internal' do
|
||||||
|
let(:record) do
|
||||||
|
ticket = create(:ticket, group: group, customer: ticket_customer)
|
||||||
|
create(:ticket_article, ticket: ticket, internal: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when agent' do
|
||||||
|
let(:user) { create(:agent, groups: [group]) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when agent and customer' do
|
||||||
|
let(:user) { create(:agent_and_customer, groups: [group]) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when customer' do
|
||||||
|
let(:user) { ticket_customer }
|
||||||
|
|
||||||
|
it { is_expected.not_to permit_actions(%i[show]) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when agent' do
|
||||||
|
let(:user) { create(:agent, groups: [group]) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when agent and customer' do
|
||||||
|
let(:user) { create(:agent_and_customer, groups: [group]) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when customer' do
|
||||||
|
let(:user) { ticket_customer }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -11,6 +11,12 @@ describe TicketPolicy do
|
||||||
it { is_expected.to permit_actions(%i[show full]) }
|
it { is_expected.to permit_actions(%i[show full]) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when given user that is agent and customer' do
|
||||||
|
let(:user) { create(:agent_and_customer, groups: [record.group]) }
|
||||||
|
|
||||||
|
it { is_expected.to permit_actions(%i[show full]) }
|
||||||
|
end
|
||||||
|
|
||||||
context 'when given a user that is neither owner nor customer' do
|
context 'when given a user that is neither owner nor customer' do
|
||||||
let(:user) { create(:agent) }
|
let(:user) { create(:agent) }
|
||||||
|
|
||||||
|
|
|
@ -877,4 +877,151 @@ RSpec.describe 'Ticket zoom', type: :system do
|
||||||
expect(images_identical?(images.first, images.second)).to be(true)
|
expect(images_identical?(images.first, images.second)).to be(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'object manager attribute permission view' do
|
||||||
|
let!(:group_users) { Group.find_by(name: 'Users') }
|
||||||
|
|
||||||
|
shared_examples 'shows attributes and values for agent view and editable' do
|
||||||
|
it 'shows attributes and values for agent view and editable', authenticated_as: :current_user do
|
||||||
|
visit "ticket/zoom/#{ticket.id}"
|
||||||
|
refresh # refresh to have assets generated for ticket
|
||||||
|
|
||||||
|
expect(page).to have_select('state_id', options: ['new', 'open', 'pending reminder', 'pending close', 'closed'])
|
||||||
|
expect(page).to have_select('priority_id')
|
||||||
|
expect(page).to have_select('owner_id')
|
||||||
|
expect(page).to have_css('div.tabsSidebar-tab[data-tab=customer]')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'shows attributes and values for agent view but disabled' do
|
||||||
|
it 'shows attributes and values for agent view but disabled', authenticated_as: :current_user do
|
||||||
|
visit "ticket/zoom/#{ticket.id}"
|
||||||
|
refresh # refresh to have assets generated for ticket
|
||||||
|
|
||||||
|
expect(page).to have_css('select[name=state_id][disabled]')
|
||||||
|
expect(page).to have_css('select[name=priority_id][disabled]')
|
||||||
|
expect(page).to have_css('select[name=owner_id][disabled]')
|
||||||
|
expect(page).to have_css('div.tabsSidebar-tab[data-tab=customer]')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'shows attributes and values for customer view' do
|
||||||
|
it 'shows attributes and values for customer view', authenticated_as: :current_user do
|
||||||
|
visit "ticket/zoom/#{ticket.id}"
|
||||||
|
refresh # refresh to have assets generated for ticket
|
||||||
|
|
||||||
|
expect(page).to have_select('state_id', options: %w[new open closed])
|
||||||
|
expect(page).not_to have_select('priority_id')
|
||||||
|
expect(page).not_to have_select('owner_id')
|
||||||
|
expect(page).not_to have_css('div.tabsSidebar-tab[data-tab=customer]')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as customer' do
|
||||||
|
let!(:current_user) { create(:customer) }
|
||||||
|
let(:ticket) { create(:ticket, customer: current_user) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for customer view'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as agent with full permissions' do
|
||||||
|
let(:current_user) { create(:agent, groups: [ group_users ] ) }
|
||||||
|
let(:ticket) { create(:ticket, group: group_users ) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view and editable'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as agent with change permissions' do
|
||||||
|
let!(:current_user) { create(:agent) }
|
||||||
|
let(:ticket) { create(:ticket, group: group_users) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
current_user.group_names_access_map = {
|
||||||
|
group_users.name => %w[read change],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view and editable'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as agent with read permissions' do
|
||||||
|
let!(:current_user) { create(:agent) }
|
||||||
|
let(:ticket) { create(:ticket, group: group_users) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
current_user.group_names_access_map = {
|
||||||
|
group_users.name => 'read',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view but disabled'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as agent+customer with full permissions' do
|
||||||
|
let!(:current_user) { create(:agent_and_customer, groups: [ group_users ] ) }
|
||||||
|
|
||||||
|
context 'normal ticket' do
|
||||||
|
let(:ticket) { create(:ticket, group: group_users ) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view and editable'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'ticket where current_user is also customer' do
|
||||||
|
let(:ticket) { create(:ticket, customer: current_user, group: group_users ) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view and editable'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as agent+customer with change permissions' do
|
||||||
|
let!(:current_user) { create(:agent_and_customer) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
current_user.group_names_access_map = {
|
||||||
|
group_users.name => %w[read change],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'normal ticket' do
|
||||||
|
let(:ticket) { create(:ticket, group: group_users) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view and editable'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'ticket where current_user is also customer' do
|
||||||
|
let(:ticket) { create(:ticket, customer: current_user, group: group_users) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view and editable'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as agent+customer with read permissions' do
|
||||||
|
let!(:current_user) { create(:agent_and_customer) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
current_user.group_names_access_map = {
|
||||||
|
group_users.name => 'read',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'normal ticket' do
|
||||||
|
let(:ticket) { create(:ticket, group: group_users) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view but disabled'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'ticket where current_user is also customer' do
|
||||||
|
let(:ticket) { create(:ticket, customer: current_user, group: group_users) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for agent view but disabled'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as agent+customer but only customer for the ticket (no agent access)' do
|
||||||
|
let!(:current_user) { create(:agent_and_customer) }
|
||||||
|
let(:ticket) { create(:ticket, customer: current_user) }
|
||||||
|
|
||||||
|
include_examples 'shows attributes and values for customer view'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -123,7 +123,7 @@ class TicketOverviewOutOfOfficeTest < ActiveSupport::TestCase
|
||||||
link: 'my_tickets',
|
link: 'my_tickets',
|
||||||
prio: 1100,
|
prio: 1100,
|
||||||
role_ids: [overview_role.id],
|
role_ids: [overview_role.id],
|
||||||
out_of_office: true,
|
out_of_office: false,
|
||||||
condition: {
|
condition: {
|
||||||
'ticket.state_id' => {
|
'ticket.state_id' => {
|
||||||
operator: 'is',
|
operator: 'is',
|
||||||
|
|
|
@ -651,12 +651,6 @@ class UserTest < ActiveSupport::TestCase
|
||||||
created_by_id: 1,
|
created_by_id: 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_raises(RuntimeError) do
|
|
||||||
customer3.roles = Role.where(name: %w[Customer Admin])
|
|
||||||
end
|
|
||||||
assert_raises(RuntimeError) do
|
|
||||||
customer3.roles = Role.where(name: %w[Customer Agent])
|
|
||||||
end
|
|
||||||
customer3.roles = Role.where(name: %w[Admin Agent])
|
customer3.roles = Role.where(name: %w[Admin Agent])
|
||||||
customer3.roles.each do |role|
|
customer3.roles.each do |role|
|
||||||
assert_not_equal(role.name, 'Customer')
|
assert_not_equal(role.name, 'Customer')
|
||||||
|
|
Loading…
Reference in a new issue