Added twitter length count support in new article.

This commit is contained in:
Martin Edenhofer 2016-01-13 00:55:17 +01:00
parent c91b782f16
commit 7f27fefdb6
6 changed files with 241 additions and 101 deletions

View file

@ -319,7 +319,7 @@ class App.TicketZoom extends App.Controller
@form_id = App.ControllerForm.formId()
new App.TicketZoomArticleNew(
@articleNew = new App.TicketZoomArticleNew(
ticket: @ticket
ticket_id: @ticket.id
el: elLocal.find('.article-new')
@ -495,12 +495,15 @@ class App.TicketZoom extends App.Controller
e.stopPropagation()
e.preventDefault()
# validate new article
return if !@articleNew.validate()
taskAction = @$('.js-secondaryActionButtonLabel').data('type')
ticketParams = @formParam( @$('.edit') )
# validate ticket
ticket = App.Ticket.fullLocal( @ticket.id )
ticket = App.Ticket.fullLocal(@ticket.id)
# reset article - should not be resubmited on next ticket update
ticket.article = undefined
@ -564,76 +567,9 @@ class App.TicketZoom extends App.Controller
console.log('ticket validateion ok')
# validate article
articleParams = @formParam( @$('.article-add') )
console.log 'submit article', articleParams
# check if attachment exists but no body
attachmentCount = @$('.article-add .textBubble .attachments .attachment').length
if !articleParams['body'] && attachmentCount > 0
new App.ControllerModal(
head: 'Text missing'
buttonCancel: 'Cancel'
buttonCancelClass: 'btn--danger'
buttonSubmit: false
message: 'Please fill also some text in!'
shown: true
small: true
container: @el.closest('.content')
)
@formEnable(e)
@autosaveStart()
return
if articleParams['body']
articleParams.from = @Session.get().displayName()
articleParams.ticket_id = ticket.id
articleParams.form_id = @form_id
articleParams.content_type = 'text/html'
if !articleParams['internal']
articleParams['internal'] = false
if @isRole('Customer')
sender = App.TicketArticleSender.findByAttribute( 'name', 'Customer' )
type = App.TicketArticleType.findByAttribute( 'name', 'web' )
articleParams.type_id = type.id
articleParams.sender_id = sender.id
else
sender = App.TicketArticleSender.findByAttribute( 'name', 'Agent' )
articleParams.sender_id = sender.id
type = App.TicketArticleType.findByAttribute( 'name', articleParams['type'] )
articleParams.type_id = type.id
articleParams = @articleNew.params()
if articleParams
article = new App.TicketArticle
for key, value of articleParams
article[key] = value
# validate email params
if type.name is 'email'
# check if recipient exists
if !articleParams['to'] && !articleParams['cc']
alert( App.i18n.translateContent('Need recipient in "To" or "Cc".') )
@formEnable(e)
@autosaveStart()
return
# check if message exists
if !articleParams['body']
alert( App.i18n.translateContent('Text needed') )
@formEnable(e)
@autosaveStart()
return
# check attachment
if articleParams['body']
if App.Utils.checkAttachmentReference( articleParams['body'] ) && attachmentCount < 1
if !confirm( App.i18n.translateContent('You use attachment in text but no attachment is attached. Do you want to continue?') )
@formEnable(e)
@autosaveStart()
return
article.load(articleParams)
errors = article.validate()
if errors
@ -721,7 +657,7 @@ class App.TicketZoom extends App.Controller
App.Ajax.request(
type: 'DELETE'
url: App.Config.get('api_path') + '/ticket_attachment_upload'
data: JSON.stringify( { form_id: @form_id } )
data: JSON.stringify(form_id: @form_id)
processData: false
)

View file

@ -12,6 +12,8 @@ class App.TicketZoomArticleNew extends App.Controller
'.js-cancel': 'cancelContainer'
'.textBubble': 'textBubble'
'.editControls-item': 'editControlItem'
'.js-letterCount': 'letterCount'
'.js-signature': 'signature'
events:
'click .js-toggleVisibility': 'toggleVisibility'
@ -21,6 +23,7 @@ class App.TicketZoomArticleNew extends App.Controller
'click .js-writeArea': 'propagateOpenTextarea'
'click .list-entry-type div': 'changeType'
'focus .js-textarea': 'openTextarea'
'input .js-textarea': 'updateLetterCount'
constructor: ->
super
@ -32,11 +35,13 @@ class App.TicketZoomArticleNew extends App.Controller
name: 'note'
icon: 'note'
attributes: []
features: ['attachment']
},
{
name: 'email'
icon: 'email'
attributes: ['to','cc']
attributes: ['to', 'cc']
features: ['attachment']
},
{
name: 'facebook'
@ -44,21 +49,29 @@ class App.TicketZoomArticleNew extends App.Controller
attributes: []
},
{
name: 'twitter status'
icon: 'twitter'
attributes: []
name: 'twitter status'
icon: 'twitter'
attributes: []
features: ['body:limit']
maxTextLength: 140
warningTextLength: 30
},
{
name: 'twitter direct-message'
icon: 'twitter'
attributes: ['to']
name: 'twitter direct-message'
icon: 'twitter'
attributes: ['to']
features: ['body:limit']
maxTextLength: 10000
warningTextLength: 500
},
{
name: 'phone'
icon: 'phone'
attributes: []
features: ['attachment']
},
]
if @isRole('Customer')
@type = 'note'
@articleTypes = [
@ -66,6 +79,7 @@ class App.TicketZoomArticleNew extends App.Controller
name: 'note'
icon: 'note'
attributes: []
features: ['attachment']
},
]
@ -216,6 +230,106 @@ class App.TicketZoomArticleNew extends App.Controller
)
@subscribeIdTextModule = ticket.subscribe( callback )
params: =>
params = @formParam( @$('.article-add') )
if params.body
params.from = @Session.get().displayName()
params.ticket_id = @ticket_id
params.form_id = @form_id
params.content_type = 'text/html'
if !params['internal']
params['internal'] = false
if @isRole('Customer')
sender = App.TicketArticleSender.findByAttribute('name', 'Customer')
type = App.TicketArticleType.findByAttribute('name', 'web')
params.type_id = type.id
params.sender_id = sender.id
else
sender = App.TicketArticleSender.findByAttribute('name', 'Agent')
params.sender_id = sender.id
type = App.TicketArticleType.findByAttribute('name', params['type'])
params.type_id = type.id
if params.type is 'twitter status'
App.Utils.htmlRemoveRichtext(@$('[data-name=body]'))
params.content_type = 'text/plain'
params.body = "#{App.Utils.html2text(params.body, true)}\n#{@signature.text()}"
if params.type is 'twitter direct-message'
App.Utils.htmlRemoveRichtext(@$('[data-name=body]'))
params.content_type = 'text/plain'
params.body = "#{App.Utils.html2text(params.body, true)}\n#{@signature.text()}"
params
validate: =>
params = @params()
# check if attachment exists but no body
attachmentCount = @$('.article-add .textBubble .attachments .attachment').length
if !params.body && attachmentCount > 0
new App.ControllerModal(
head: 'Text missing'
buttonCancel: 'Cancel'
buttonCancelClass: 'btn--danger'
buttonSubmit: false
message: 'Please fill also some text in!'
shown: true
small: true
container: @el.closest('.content')
)
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
if App.Utils.checkAttachmentReference(params.body) && attachmentCount < 1
if !confirm( App.i18n.translateContent('You use attachment in text but no attachment is attached. Do you want to continue?') )
return false
if params.type is 'twitter status'
params.body
textLength = @maxTextLength - params.body.length
return false if textLength < 0
if params.type is 'twitter direct-message'
textLength = @maxTextLength - params.body.length
return false if textLength < 0
true
changeType: (e) ->
$(e.target).addClass('active').siblings('.active').removeClass('active')
@ -232,7 +346,6 @@ class App.TicketZoomArticleNew extends App.Controller
.addClass 'is-public'
.removeClass 'is-internal'
@$('[name=internal]').val ''
showSelectableArticleType: (event) =>
@ -243,7 +356,7 @@ class App.TicketZoomArticleNew extends App.Controller
selectArticleType: (event) =>
event.stopPropagation()
articleTypeToSet = $(event.target).closest('.pop-selectable').data('value')
@setArticleType( articleTypeToSet )
@setArticleType(articleTypeToSet)
@hideSelectableArticleType()
$(window).off 'click.ticket-zoom-select-type'
@ -251,20 +364,13 @@ class App.TicketZoomArticleNew extends App.Controller
hideSelectableArticleType: =>
@el.find('.js-articleTypes').addClass('is-hidden')
setArticleType: (type) ->
setArticleType: (type) =>
wasScrolledToBottom = @isScrolledToBottom()
@type = type
@$('[name=type]').val(type)
@articleNewEdit.attr('data-type', type)
@$('.js-selectableTypes').addClass('hide').filter("[data-type='#{ type }']").removeClass('hide')
# show/hide attributes
for articleType in @articleTypes
if articleType.name is type
@$('.form-group').addClass('hide')
for name in articleType.attributes
@$("[name=#{name}]").closest('.form-group').removeClass('hide')
# detect current signature (use current group_id, if not set, use ticket.group_id)
ticketCurrent = App.Ticket.find(@ticket_id)
group_id = ticketCurrent.group_id
@ -299,6 +405,31 @@ class App.TicketZoomArticleNew extends App.Controller
else
@$('[data-name=body] [data-signature=true]').remove()
# remove richtext
if @type is 'twitter status'
App.Utils.htmlRemoveRichtext(@$('[data-name=body]'))
if @type is 'twitter direct-message'
App.Utils.htmlRemoveRichtext(@$('[data-name=body]'))
# show/hide attributes/features
@maxTextLength = undefined
@warningTextLength = undefined
for articleType in @articleTypes
if articleType.name is type
@$('.form-group').addClass('hide')
for name in articleType.attributes
@$("[name=#{name}]").closest('.form-group').removeClass('hide')
@$('.article-attachment, .attachments, .js-textSizeLimit').addClass('hide')
for name in articleType.features
if name is 'attachment'
@$('.article-attachment, .attachments').removeClass('hide')
if name is 'body:limit'
@maxTextLength = articleType.maxTextLength
@warningTextLength = articleType.warningTextLength
@delay(@updateLetterCount, 600)
@updateInitials()
@$('.js-textSizeLimit').removeClass('hide')
@scrollToBottom() if wasScrolledToBottom
isScrolledToBottom: ->
@ -311,6 +442,26 @@ class App.TicketZoomArticleNew extends App.Controller
event.stopPropagation()
@textarea.focus()
updateLetterCount: =>
return if !@maxTextLength
return if !@warningTextLength
params = @params()
textLength = @maxTextLength - params.body.length
className = switch
when textLength < 0 then 'label-danger'
when textLength < @warningTextLength then 'label-warning'
else ''
@letterCount
.text textLength
.removeClass 'label-danger label-warning'
.addClass className
updateInitials: (value) =>
if value is undefined
value = "/#{App.User.find(@Session.get('id')).initials()}"
@signature.text(value)
openTextarea: (event, withoutAnimation) =>
if event
event.stopPropagation()
@ -450,7 +601,7 @@ class App.TicketZoomArticleNew extends App.Controller
App.Ajax.request(
type: 'DELETE'
url: App.Config.get('api_path') + '/ticket_attachment_upload'
data: JSON.stringify( { store_id: store_id } )
data: JSON.stringify(store_id: store_id)
processData: false
)

View file

@ -17,24 +17,31 @@ class App.Utils
ascii = '<div>' + ascii.replace(/\n/g, '</div><div>') + '</div>'
ascii.replace(/<div><\/div>/g, '<div><br></div>')
# rawText = App.Utils.html2text(html)
@html2text: (html) ->
# rawText = App.Utils.html2text(html, no_trim)
@html2text: (html, no_trim) ->
if no_trim
html = html
.replace(/([A-z])\n([A-z])/gm, '$1 $2')
.replace(/\n|\r/g, '')
.replace(/<(br|hr)>/g, "\n")
.replace(/<(br|hr)\/>/g, "\n")
.replace(/<\/(div|p|blockquote|form|textarea|address|tr)>/g, "\n")
return $('<div>' + html + '</div>').text()
# remove not needed new lines
html = html.replace(/>\n/g, '>')
html = html.replace(/([A-z])\n([A-z])/gm, '$1 $2')
.replace(/>\n/g, '>')
.replace(/\n|\r/g, '')
# insert new lines
# trim and cleanup
html = html
.replace(/<br(|.+?)>/g, "\n")
.replace(/<br\/>/g, "\n")
.replace(/<(br|hr)>/g, "\n")
.replace(/<(br|hr)\/>/g, "\n")
.replace(/<(div)(|.+?)>/g, "")
.replace(/<(p|blockquote|form|textarea|address|tr)(|.+?)>/g, "\n")
.replace(/<\/(div|p|blockquote|form|textarea|address|tr)>/g, "\n")
# trim and cleanup
$('<div>' + html + '</div>').text().trim()
.replace(/(\r\n|\n\r)/g, "\n") # cleanup
.replace(/\r/g, "\n") # cleanup
.replace(/\n{3,20}/g, "\n\n") # remove multiple empty lines
# htmlEscapedAndLinkified = App.Utils.linkify(rawText)

View file

@ -52,6 +52,10 @@
<div class="bubble-arrow"></div>
<div class="js-textarea articleNewEdit-body" contenteditable="true" data-name="body"><%- @article.body %></div>
<!-- .textBubble grows with textarea (and expanding clone) -->
<div class="textBubble-footer js-textSizeLimit">
<div class="textBubble-signatur"><span class="js-signature"></span></div>
<div class="textBubble-letterCount js-letterCount"></div>
</div>
<div class="attachments"></div>
<div class="article-attachment">
<div class="attachmentPlaceholder">

View file

@ -25,7 +25,7 @@ class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
tweet = channel.deliver(
type: type['name'],
to: record.to,
body: record.body.html2text,
body: record.body,
in_reply_to: record.in_reply_to
)

View file

@ -113,6 +113,48 @@ test("html2text", function() {
should = "test 123\nlalala\n--\nsome test"
result = App.Utils.html2text(source)
equal(result, should, source)
source = "<p><span>Was\nsoll verbessert werden:</span></p>"
should = "Was soll verbessert werden:"
result = App.Utils.html2text(source)
equal(result, should, source)
// in raw format, without cleanup
source = "<div>Some</div><div>1234</div>"
should = "Some\n1234\n"
result = App.Utils.html2text(source, true)
equal(result, should, source)
source = "<div>Some</div><div> 1234</div>"
should = "Some\n 1234\n"
result = App.Utils.html2text(source, true)
equal(result, should, source)
source = "\n\n<div>Some</div>\n<div> 1234</div>"
should = "Some\n 1234\n"
result = App.Utils.html2text(source, true)
equal(result, should, source)
source = "<div>Some</div><div> 1234</div>"
should = "Some\n 1234\n"
result = App.Utils.html2text(source, true)
equal(result, should, source)
source = "<div>Some</div>\n\n<div> 1234</div>\n"
should = "Some\n 1234\n"
result = App.Utils.html2text(source, true)
equal(result, should, source)
source = "<div>test<br>new line<br></div>"
should = "test\nnew line\n\n"
result = App.Utils.html2text(source, true)
equal(result, should, source)
source = "<p><span>Was\nsoll verbessert werden:</span></p>"
should = "Was soll verbessert werden:\n"
result = App.Utils.html2text(source, true)
equal(result, should, source)
});
// linkify