Fixed issue #1857 - Missing To and Cc field when forwarding an article of a customer without an email address.

This commit is contained in:
Martin Edenhofer 2018-03-06 06:31:53 +01:00
parent 7e1b25468f
commit 008cfeea69
7 changed files with 307 additions and 209 deletions

View file

@ -1,15 +1,18 @@
class EmailReply extends App.Controller
@action: (actions, ticket, article, ui) ->
return actions if ui.permissionCheck('ticket.customer')
return actions if !ui.permissionCheck('ticket.agent')
group = ticket.group
if group.email_address_id && (article.type.name is 'email' || article.type.name is 'web')
return actions if !group.email_address_id
if article.type.name is 'email' || article.type.name is 'web'
actions.push {
name: 'reply'
type: 'emailReply'
icon: 'reply'
href: '#'
}
# check if reply all need to be shown
recipients = []
if article.sender.name is 'Customer'
if article.from
@ -51,6 +54,7 @@ class EmailReply extends App.Controller
href: '#'
}
# always show forward
actions.push {
name: 'forward'
type: 'emailForward'
@ -113,6 +117,12 @@ class EmailReply extends App.Controller
# empty form
articleNew = App.Utils.getRecipientArticle(ticket, article, article_created_by, type, email_addresses, all)
if ui.Config.get('ui_ticket_zoom_article_email_subject')
if _.isEmpty(article.subject)
articleNew.subject = ticket.title
else
articleNew.subject = article.subject
# get current body
body = ui.el.closest('.ticketZoom').find('.article-add [data-name="body"]').html() || ''
@ -172,6 +182,12 @@ class EmailReply extends App.Controller
articleNew = {}
articleNew.body = body
if ui.Config.get('ui_ticket_zoom_article_email_subject')
if _.isEmpty(article.subject)
articleNew.subject = "FW: #{ticket.title}"
else
articleNew.subject = "FW: #{article.subject}"
type = App.TicketArticleType.findByAttribute(name:'email')
App.Event.trigger('ui::ticket::setArticleType', {
@ -200,4 +216,100 @@ class EmailReply extends App.Controller
true
@articleTypes: (articleTypes, ticket, ui) ->
return articleTypes if !ui.permissionCheck('ticket.agent')
group = ticket.group
return articleTypes if !group.email_address_id
attributes = ['to', 'cc', 'subject']
if !ui.Config.get('ui_ticket_zoom_article_email_subject')
attributes = ['to', 'cc']
articleTypes.push {
name: 'email'
icon: 'email'
attributes: attributes
internal: false,
features: ['attachment']
}
articleTypes
@setArticleType: (type, ticket, ui, signaturePosition) ->
# detect current signature (use current group_id, if not set, use ticket.group_id)
ticketCurrent = App.Ticket.fullLocal(ticket.id)
group_id = ticketCurrent.group_id
task = App.TaskManager.get(ui.taskKey)
if task && task.state && task.state.ticket && task.state.ticket.group_id
group_id = task.state.ticket.group_id
group = App.Group.find(group_id)
signature = undefined
if group && group.signature_id
signature = App.Signature.find(group.signature_id)
# add/replace signature
if signature && signature.body && type is 'email'
# if signature has changed, remove it
signature_id = ui.$('[data-signature=true]').data('signature-id')
if signature_id && signature_id.toString() isnt signature.id.toString()
ui.$('[data-name=body] [data-signature="true"]').remove()
# apply new signature
signatureFinished = App.Utils.replaceTags(signature.body, { user: App.Session.get(), ticket: ticketCurrent, config: App.Config.all() })
body = ui.$('[data-name=body]')
if App.Utils.signatureCheck(body.html() || '', signatureFinished)
if !App.Utils.htmlLastLineEmpty(body)
body.append('<br><br>')
signature = $("<div data-signature=\"true\" data-signature-id=\"#{signature.id}\">#{signatureFinished}</div>")
App.Utils.htmlStrip(signature)
if signaturePosition is 'top'
body.prepend(signature)
else
body.append(signature)
ui.$('[data-name=body]').replaceWith(body)
# remove old signature
else
ui.$('[data-name=body] [data-signature=true]').remove()
if type isnt 'email'
ui.$('[name=to]').val('')
ui.$('[name=cc]').val('')
ui.$('[name=subject]').val('')
@validation: (type, params, ui) ->
return true if type isnt 'email'
# check if recipient exists
if _.isEmpty(params.to) && _.isEmpty(params.cc)
new App.ControllerModal(
head: 'Text missing'
buttonCancel: 'Cancel'
buttonCancelClass: 'btn--danger'
buttonSubmit: false
message: 'Need recipient in "To" or "Cc".'
shown: true
small: true
container: ui.el.closest('.content')
)
return false
# check if message exists
if _.isEmpty(params.body)
new App.ControllerModal(
head: 'Text missing'
buttonCancel: 'Cancel'
buttonCancelClass: 'btn--danger'
buttonSubmit: false
message: 'Text needed'
shown: true
small: true
container: ui.el.closest('.content')
)
return false
true
App.Config.set('200-EmailReply', EmailReply, 'TicketZoomArticleAction')

View file

@ -34,4 +34,28 @@ class FacebookReply
true
@articleTypes: (articleTypes, ticket, ui) ->
return articleTypes if !ui.permissionCheck('ticket.agent')
return articleTypes if !ticket || !ticket.create_article_type_id
articleTypeCreate = App.TicketArticleType.find(ticket.create_article_type_id).name
if articleTypeCreate is 'facebook feed post'
articleTypes.push {
name: 'facebook feed comment'
icon: 'facebook'
attributes: []
internal: false,
features: []
}
articleTypes
@params: (type, params, ui) ->
if type is 'facebook feed comment'
App.Utils.htmlRemoveRichtext(ui.$('[data-name=body]'), false)
params.content_type = 'text/plain'
params.body = App.Utils.html2text(params.body, true)
params
App.Config.set('300-FacebookReply', FacebookReply, 'TicketZoomArticleAction')

View file

@ -0,0 +1,23 @@
class Note
@action: (actions, ticket, article, ui) ->
actions
@perform: (articleContainer, type, ticket, article, ui) ->
true
@articleTypes: (articleTypes, ticket, ui) ->
internal = false
if ui.permissionCheck('ticket.agent')
internal = ui.Config.get('ui_ticket_zoom_article_note_new_internal')
articleTypes.push {
name: 'note'
icon: 'note'
attributes: []
internal: internal,
features: ['attachment']
}
articleTypes
App.Config.set('100-Note', Note, 'TicketZoomArticleAction')

View file

@ -0,0 +1,19 @@
class PhoneReply
@action: (actions, ticket, article, ui) ->
actions
@perform: (articleContainer, type, ticket, article, ui) ->
true
@articleTypes: (articleTypes, ticket, ui) ->
return articleTypes if !ui.permissionCheck('ticket.agent')
articleTypes.push {
name: 'phone'
icon: 'phone'
attributes: []
internal: false,
features: ['attachment']
}
articleTypes
App.Config.set('100-PhoneReply', PhoneReply, 'TicketZoomArticleAction')

View file

@ -42,4 +42,38 @@ class TelegramReply
true
@articleTypes: (articleTypes, ticket, ui) ->
return articleTypes if !ui.permissionCheck('ticket.agent')
return articleTypes if !ticket || !ticket.create_article_type_id
articleTypeCreate = App.TicketArticleType.find(ticket.create_article_type_id).name
return articleTypes if articleTypeCreate isnt 'telegram personal-message'
articleTypes.push {
name: 'telegram personal-message'
icon: 'telegram'
attributes: []
internal: false,
features: ['attachment']
maxTextLength: 10000
warningTextLength: 5000
}
articleTypes
@setArticleType: (type, ticket, ui) ->
return if type isnt 'telegram personal-message'
rawHTML = ui.$('[data-name=body]').html()
cleanHTML = App.Utils.htmlRemoveRichtext(rawHTML)
if cleanHTML && cleanHTML.html() != rawHTML
ui.$('[data-name=body]').html(cleanHTML)
@params: (type, params, ui) ->
if type is 'telegram personal-message'
App.Utils.htmlRemoveRichtext(ui.$('[data-name=body]'), false)
params.content_type = 'text/plain'
params.body = App.Utils.html2text(params.body, true)
params
App.Config.set('300-TelegramReply', TelegramReply, 'TicketZoomArticleAction')

View file

@ -125,4 +125,70 @@ class TwitterReply
article: articleNew
})
@articleTypes: (articleTypes, ticket, ui) ->
return articleTypes if !ui.permissionCheck('ticket.agent')
return articleTypes if !ticket || !ticket.create_article_type_id
articleTypeCreate = App.TicketArticleType.find(ticket.create_article_type_id).name
if articleTypeCreate is 'twitter status'
attributes = ['body:limit', 'body:initials']
if !ui.Config.get('ui_ticket_zoom_article_twitter_initials')
attributes = ['body:limit']
articleTypes.push {
name: 'twitter status'
icon: 'twitter'
attributes: []
internal: false,
features: attributes
maxTextLength: 280
warningTextLength: 30
}
else if articleTypeCreate is 'twitter direct-message'
attributes = ['body:limit', 'body:initials']
if !ui.Config.get('ui_ticket_zoom_article_twitter_initials')
attributes = ['body:limit']
articleTypes.push {
name: 'twitter direct-message'
icon: 'twitter'
attributes: ['to']
internal: false,
features: attributes
maxTextLength: 10000
warningTextLength: 500
}
articleTypes
@validation: (type, params, ui) ->
if type is 'twitter status'
textLength = ui.maxTextLength - App.Utils.textLengthWithUrl(params.body)
return false if textLength < 0
if params.type is 'twitter direct-message'
textLength = ui.maxTextLength - App.Utils.textLengthWithUrl(params.body)
return false if textLength < 0
true
@setArticleType: (type, ticket, ui) ->
return if type isnt 'twitter status' && type isnt 'twitter direct-message'
rawHTML = ui.$('[data-name=body]').html()
cleanHTML = App.Utils.htmlRemoveRichtext(rawHTML)
if cleanHTML && cleanHTML.html() != rawHTML
ui.$('[data-name=body]').html(cleanHTML)
@params: (type, params, ui) ->
if type is 'twitter status'
App.Utils.htmlRemoveRichtext(ui.$('[data-name=body]'), false)
params.content_type = 'text/plain'
params.body = App.Utils.html2text(params.body, true)
if type is 'twitter direct-message'
App.Utils.htmlRemoveRichtext(ui.$('[data-name=body]'), false)
params.content_type = 'text/plain'
params.body = App.Utils.html2text(params.body, true)
params
App.Config.set('300-TwitterReply', TwitterReply, 'TicketZoomArticleAction')

View file

@ -92,112 +92,13 @@ class App.TicketZoomArticleNew extends App.Controller
)
setPossibleArticleTypes: =>
possibleArticleType =
note: true
phone: true
if @ticket && @ticket.create_article_type_id
articleTypeCreate = App.TicketArticleType.find(@ticket.create_article_type_id).name
if articleTypeCreate is 'twitter status'
possibleArticleType['twitter status'] = true
else if articleTypeCreate is 'twitter direct-message'
possibleArticleType['twitter direct-message'] = true
else if articleTypeCreate is 'email'
possibleArticleType['email'] = true
else if articleTypeCreate is 'facebook feed post'
possibleArticleType['facebook feed comment'] = true
else if articleTypeCreate is 'telegram personal-message'
possibleArticleType['telegram personal-message'] = true
if @ticket && @ticket.customer_id
customer = App.User.find(@ticket.customer_id)
if customer.email
possibleArticleType['email'] = true
# gets referenced in @setArticleType
actionConfig = App.Config.get('TicketZoomArticleAction')
keys = _.keys(actionConfig).sort()
@articleTypes = []
if possibleArticleType.note
internal = @Config.get('ui_ticket_zoom_article_note_new_internal')
@articleTypes.push {
name: 'note'
icon: 'note'
attributes: []
internal: internal,
features: ['attachment']
}
if possibleArticleType.email
attributes = ['to', 'cc', 'subject']
if !@Config.get('ui_ticket_zoom_article_email_subject')
attributes = ['to', 'cc']
@articleTypes.push {
name: 'email'
icon: 'email'
attributes: attributes
internal: false,
features: ['attachment']
}
if possibleArticleType['facebook feed comment']
@articleTypes.push {
name: 'facebook feed comment'
icon: 'facebook'
attributes: []
internal: false,
features: []
}
if possibleArticleType['twitter status']
attributes = ['body:limit', 'body:initials']
if !@Config.get('ui_ticket_zoom_article_twitter_initials')
attributes = ['body:limit']
@articleTypes.push {
name: 'twitter status'
icon: 'twitter'
attributes: []
internal: false,
features: attributes
maxTextLength: 280
warningTextLength: 30
}
if possibleArticleType['twitter direct-message']
attributes = ['body:limit', 'body:initials']
if !@Config.get('ui_ticket_zoom_article_twitter_initials')
attributes = ['body:limit']
@articleTypes.push {
name: 'twitter direct-message'
icon: 'twitter'
attributes: ['to']
internal: false,
features: attributes
maxTextLength: 10000
warningTextLength: 500
}
if possibleArticleType.phone
@articleTypes.push {
name: 'phone'
icon: 'phone'
attributes: []
internal: false,
features: ['attachment']
}
if possibleArticleType['telegram personal-message']
@articleTypes.push {
name: 'telegram personal-message'
icon: 'telegram'
attributes: []
internal: false,
features: ['attachment']
maxTextLength: 10000
warningTextLength: 5000
}
if @permissionCheck('ticket.customer')
@type = 'note'
@articleTypes = [
{
name: 'note'
icon: 'note'
attributes: []
internal: false,
features: ['attachment']
},
]
for key in keys
config = actionConfig[key]
if config && config.articleTypes
@articleTypes = config.articleTypes(@articleTypes, @ticket, @)
placeCaretAtEnd: (el) ->
el.focus()
@ -356,25 +257,13 @@ class App.TicketZoomArticleNew extends App.Controller
else
params.internal = false
if params.type is 'twitter status'
App.Utils.htmlRemoveRichtext(@$('[data-name=body]'), false)
params.content_type = 'text/plain'
params.body = App.Utils.html2text(params.body, true)
if params.type is 'twitter direct-message'
App.Utils.htmlRemoveRichtext(@$('[data-name=body]'), false)
params.content_type = 'text/plain'
params.body = App.Utils.html2text(params.body, true)
if params.type is 'facebook feed comment'
App.Utils.htmlRemoveRichtext(@$('[data-name=body]'), false)
params.content_type = 'text/plain'
params.body = App.Utils.html2text(params.body, true)
if params.type is 'telegram personal-message'
App.Utils.htmlRemoveRichtext(@$('[data-name=body]'), false)
params.content_type = 'text/plain'
params.body = App.Utils.html2text(params.body, true)
# backend based validation
actionConfig = App.Config.get('TicketZoomArticleAction')
keys = _.keys(actionConfig).sort()
for key in keys
config = actionConfig[key]
if config && config.params
params = config.params(params.type, params, @)
# add initals?
for articleType in @articleTypes
@ -406,37 +295,6 @@ class App.TicketZoomArticleNew extends App.Controller
)
return false
# validate email params
if params.type is 'email'
# check if recipient exists
if !params.to && !params.cc
new App.ControllerModal(
head: 'Text missing'
buttonCancel: 'Cancel'
buttonCancelClass: 'btn--danger'
buttonSubmit: false
message: 'Need recipient in "To" or "Cc".'
shown: true
small: true
container: @el.closest('.content')
)
return false
# check if message exists
if !params.body
new App.ControllerModal(
head: 'Text missing'
buttonCancel: 'Cancel'
buttonCancelClass: 'btn--danger'
buttonSubmit: false
message: 'Text needed'
shown: true
small: true
container: @el.closest('.content')
)
return false
# check attachment
if params.body && attachmentCount < 1
matchingWord = App.Utils.checkAttachmentReference(params.body)
@ -444,13 +302,13 @@ class App.TicketZoomArticleNew extends App.Controller
if !confirm(App.i18n.translateContent('You use %s in text but no attachment is attached. Do you want to continue?', matchingWord))
return false
if params.type is 'twitter status'
textLength = @maxTextLength - App.Utils.textLengthWithUrl(params.body)
return false if textLength < 0
if params.type is 'twitter direct-message'
textLength = @maxTextLength - App.Utils.textLengthWithUrl(params.body)
return false if textLength < 0
# backend based validation
actionConfig = App.Config.get('TicketZoomArticleAction')
keys = _.keys(actionConfig).sort()
for key in keys
config = actionConfig[key]
if config && config.validation
return false if !config.validation(params.type, params, @)
true
@ -516,50 +374,12 @@ class App.TicketZoomArticleNew extends App.Controller
else
@setArticleInternal(false)
# detect current signature (use current group_id, if not set, use ticket.group_id)
ticketCurrent = App.Ticket.fullLocal(@ticket_id)
group_id = ticketCurrent.group_id
task = App.TaskManager.get(@taskKey)
if task && task.state && task.state.ticket && task.state.ticket.group_id
group_id = task.state.ticket.group_id
group = App.Group.find(group_id)
signature = undefined
if group && group.signature_id
signature = App.Signature.find(group.signature_id)
# add/replace signature
if signature && signature.body && @type is 'email'
# if signature has changed, remove it
signature_id = @$('[data-signature=true]').data('signature-id')
if signature_id && signature_id.toString() isnt signature.id.toString()
@$('[data-name=body] [data-signature="true"]').remove()
# apply new signature
signatureFinished = App.Utils.replaceTags(signature.body, { user: App.Session.get(), ticket: ticketCurrent, config: App.Config.all() })
body = @$('[data-name=body]')
if App.Utils.signatureCheck(body.html() || '', signatureFinished)
if !App.Utils.htmlLastLineEmpty(body)
body.append('<br><br>')
signature = $("<div data-signature=\"true\" data-signature-id=\"#{signature.id}\">#{signatureFinished}</div>")
App.Utils.htmlStrip(signature)
if signaturePosition is 'top'
body.prepend(signature)
else
body.append(signature)
@$('[data-name=body]').replaceWith(body)
# remove old signature
else
@$('[data-name=body] [data-signature=true]').remove()
# remove richtext
if @type is 'twitter status' || @type is 'twitter direct-message' || @type is 'telegram personal-message'
rawHTML = @$('[data-name=body]').html()
cleanHTML = App.Utils.htmlRemoveRichtext(rawHTML)
if cleanHTML && cleanHTML.html() != rawHTML
@$('[data-name=body]').html(cleanHTML)
actionConfig = App.Config.get('TicketZoomArticleAction')
keys = _.keys(actionConfig).sort()
for key in keys
localConfig = actionConfig[key]
if localConfig && localConfig.setArticleType
localConfig.setArticleType(@type, @ticket, @, signaturePosition)
# show/hide attributes/features
@maxTextLength = undefined