Browse Source

Split of article actions into separate backends.

Martin Edenhofer 7 years ago
parent
commit
f3ff650bfe

+ 34 - 0
app/assets/javascripts/app/controllers/ticket_zoom/article_action/delete.coffee

@@ -0,0 +1,34 @@
+class Delete
+  @action: (actions, ticket, article, ui) ->
+    return actions if ui.permissionCheck('ticket.customer')
+
+    if article.type.name is 'note'
+      user = undefined
+      if App.Session.get('id') == article.created_by_id
+        user = App.User.find(App.Session.get('id'))
+        if user.permission('ticket.agent')
+          actions.push {
+            name: 'delete'
+            type: 'delete'
+            icon: 'trash'
+            href: '#'
+          }
+
+    actions
+
+  @perform: (articleContainer, type, ticket, article, ui) ->
+    return true if type isnt 'delete'
+
+    callback = ->
+      article = App.TicketArticle.find(article.id)
+      article.destroy()
+
+    new App.ControllerConfirm(
+      message: 'Sure?'
+      callback: callback
+      container: ui.el.closest('.content')
+    )
+
+    true
+
+App.Config.set('900-Delete', Delete, 'TicketZoomArticleAction')

+ 135 - 0
app/assets/javascripts/app/controllers/ticket_zoom/article_action/email_reply.coffee

@@ -0,0 +1,135 @@
+class EmailReply extends App.Controller
+  @action: (actions, ticket, article, ui) ->
+    return actions if ui.permissionCheck('ticket.customer')
+
+    group = ticket.group
+    if group.email_address_id && (article.type.name is 'email' || article.type.name is 'web')
+      actions.push {
+        name: 'reply'
+        type: 'emailReply'
+        icon: 'reply'
+        href: '#'
+      }
+      recipients = []
+      if article.sender.name is 'Customer'
+        if article.from
+          localRecipients = emailAddresses.parseAddressList(article.from)
+          if localRecipients
+            recipients = recipients.concat localRecipients
+      if article.to
+        localRecipients = emailAddresses.parseAddressList(article.to)
+        if localRecipients
+          recipients = recipients.concat localRecipients
+      if article.cc
+        localRecipients = emailAddresses.parseAddressList(article.cc)
+        if localRecipients
+          recipients = recipients.concat localRecipients
+
+      # remove system addresses
+      localAddresses = App.EmailAddress.all()
+      forgeinRecipients = []
+      recipientUsed = {}
+      for recipient in recipients
+        if !_.isEmpty(recipient.address)
+          localRecipientAddress = recipient.address.toString().toLowerCase()
+          if !recipientUsed[localRecipientAddress]
+            recipientUsed[localRecipientAddress] = true
+            localAddress = false
+            for address in localAddresses
+              if localRecipientAddress is address.email.toString().toLowerCase()
+                recipientUsed[localRecipientAddress] = true
+                localAddress = true
+            if !localAddress
+              forgeinRecipients.push recipient
+
+      # check if reply all is neede
+      if forgeinRecipients.length > 1
+        actions.push {
+          name: 'reply all'
+          type: 'emailReplyAll'
+          icon: 'reply-all'
+          href: '#'
+        }
+    if article.sender.name is 'Customer' && article.type.name is 'phone'
+      actions.push {
+        name: 'reply'
+        type: 'emailReply'
+        icon: 'reply'
+        href: '#'
+      }
+    if article.sender.name is 'Agent' && article.type.name is 'phone'
+      actions.push {
+        name: 'reply'
+        type: 'emailReply'
+        icon: 'reply'
+        href: '#'
+      }
+
+    actions
+
+  @perform: (articleContainer, type, ticket, article, ui) ->
+    return true if type isnt 'emailReply' && type isnt 'emailReplyAll'
+
+    if type isnt 'emailReply'
+      @emailReply(true, ticket, article, ui)
+
+    else if type isnt 'emailReplyAll'
+      @emailReply(false, ticket, article, ui)
+
+    true
+
+  @emailReply: (all = false, ticket, article, ui) ->
+
+    # get reference article
+    type               = App.TicketArticleType.find(article.type_id)
+    article_created_by = App.User.find(article.created_by_id)
+    email_addresses    = App.EmailAddress.all()
+
+    ui.scrollToCompose()
+
+    # empty form
+    articleNew = App.Utils.getRecipientArticle(ticket, article, article_created_by, type, email_addresses, all)
+
+    # get current body
+    body = ui.el.closest('.ticketZoom').find('.article-add [data-name="body"]').html() || ''
+
+    # check if quote need to be added
+    signaturePosition = 'bottom'
+    selected = App.ClipBoard.getSelected('html')
+    if selected
+      selected = App.Utils.htmlCleanup(selected).html()
+    if !selected
+      selected = App.ClipBoard.getSelected('text')
+      if selected
+        selected = App.Utils.textCleanup(selected)
+        selected = App.Utils.text2html(selected)
+
+    # full quote, if needed
+    if !selected && article && App.Config.get('ui_ticket_zoom_article_email_full_quote')
+      signaturePosition = 'top'
+      if article.content_type.match('html')
+        selected = App.Utils.textCleanup(article.body)
+      if article.content_type.match('plain')
+        selected = App.Utils.textCleanup(article.body)
+        selected = App.Utils.text2html(selected)
+
+    if selected
+      selected = "<div><br><br/></div><div><blockquote type=\"cite\">#{selected}</blockquote></div><div><br></div>"
+
+      # add selected text to body
+      body = selected + body
+
+    articleNew.body = body
+
+    type = App.TicketArticleType.findByAttribute(name:'email')
+
+    App.Event.trigger('ui::ticket::setArticleType', {
+      ticket: ticket
+      type: type
+      article: articleNew
+      signaturePosition: signaturePosition
+    })
+
+    true
+
+App.Config.set('200-EmailReply', EmailReply, 'TicketZoomArticleAction')

+ 37 - 0
app/assets/javascripts/app/controllers/ticket_zoom/article_action/facebook_reply.coffee

@@ -0,0 +1,37 @@
+class FacebookReply
+  @action: (actions, ticket, article, ui) ->
+    return actions if ui.permissionCheck('ticket.customer')
+
+    if article.type.name is 'facebook feed post' || article.type.name is 'facebook feed comment'
+      actions.push {
+        name: 'reply'
+        type: 'facebookFeedReply'
+        icon: 'reply'
+        href: '#'
+      }
+
+    actions
+
+  @perform: (articleContainer, type, ticket, article, ui) ->
+    return true if type isnt 'facebookFeedReply'
+
+    ui.scrollToCompose()
+
+    type = App.TicketArticleType.findByAttribute('name', 'facebook feed comment')
+
+    articleNew = {
+      to:          ''
+      cc:          ''
+      body:        ''
+      in_reply_to: ''
+    }
+
+    App.Event.trigger('ui::ticket::setArticleType', {
+      ticket: ticket
+      type: type
+      article: articleNew
+    })
+
+    true
+
+App.Config.set('300-FacebookReply', FacebookReply, 'TicketZoomArticleAction')

+ 40 - 0
app/assets/javascripts/app/controllers/ticket_zoom/article_action/internal.coffee

@@ -0,0 +1,40 @@
+class Internal
+  @action: (actions, ticket, article, ui) ->
+    return actions if ui.permissionCheck('ticket.customer')
+
+    if article.internal is true
+      actions.push {
+        name: 'set to public'
+        type: 'public'
+        icon: 'lock-open'
+      }
+    else
+      actions.push {
+        name: 'set to internal'
+        type: 'internal'
+        icon: 'lock'
+      }
+
+    actions
+
+  @perform: (articleContainer, type, ticket, article, ui) ->
+    return true if type isnt 'internal' && type isnt 'public'
+
+    # storage update
+    internal = true
+    if article.internal == true
+      internal = false
+    ui.lastAttributres.internal = internal
+    article.updateAttributes(internal: internal)
+
+    # runtime update
+    if internal
+      articleContainer.addClass('is-internal')
+    else
+      articleContainer.removeClass('is-internal')
+
+    ui.render()
+
+    true
+
+App.Config.set('100-Internal', Internal, 'TicketZoomArticleAction')

+ 18 - 0
app/assets/javascripts/app/controllers/ticket_zoom/article_action/split.coffee

@@ -0,0 +1,18 @@
+class Split
+  @action: (actions, ticket, article, ui) ->
+    return actions if ui.permissionCheck('ticket.customer')
+
+    actions.push {
+      name: 'split'
+      type: 'split'
+      icon: 'split'
+      href: "#ticket/create/#{article.ticket_id}/#{article.id}"
+    }
+    actions
+
+  @perform: (articleContainer, type, ticket, article, ui) ->
+    return true if type isnt 'split'
+    ui.navigate "#ticket/create/#{article.ticket_id}/#{article.id}"
+    true
+
+App.Config.set('700-Split', Split, 'TicketZoomArticleAction')

+ 45 - 0
app/assets/javascripts/app/controllers/ticket_zoom/article_action/telegram.coffee

@@ -0,0 +1,45 @@
+class TelegramReply
+  @action: (actions, ticket, article, ui) ->
+    return actions if ui.permissionCheck('ticket.customer')
+
+    if article.sender.name is 'Customer' && article.type.name is 'telegram personal-message'
+      actions.push {
+        name: 'reply'
+        type: 'telegramPersonalMessageReply'
+        icon: 'reply'
+        href: '#'
+      }
+
+    actions
+
+  @perform: (articleContainer, type, ticket, article, ui) ->
+    return true if type isnt 'telegramPersonalMessageReply'
+
+    ui.scrollToCompose()
+
+    # get reference article
+    type = App.TicketArticleType.find(article.type_id)
+
+    articleNew = {
+      to:          ''
+      cc:          ''
+      body:        ''
+      in_reply_to: ''
+    }
+
+    if article.message_id
+      articleNew.in_reply_to = article.message_id
+
+    # get current body
+    articleNew.body = ui.el.closest('.ticketZoom').find('.article-add [data-name="body"]').html().trim() || ''
+
+    App.Event.trigger('ui::ticket::setArticleType', {
+      ticket: ticket
+      type: type
+      article: articleNew
+      position: 'end'
+    })
+
+    true
+
+App.Config.set('300-TelegramReply', TelegramReply, 'TicketZoomArticleAction')

+ 128 - 0
app/assets/javascripts/app/controllers/ticket_zoom/article_action/twitter_reply.coffee

@@ -0,0 +1,128 @@
+class TwitterReply
+  @action: (actions, ticket, article, ui) ->
+    return actions if ui.permissionCheck('ticket.customer')
+
+    if article.type.name is 'twitter status'
+      actions.push {
+        name: 'reply'
+        type: 'twitterStatusReply'
+        icon: 'reply'
+        href: '#'
+      }
+    if article.type.name is 'twitter direct-message'
+      actions.push {
+        name: 'reply'
+        type: 'twitterDirectMessageReply'
+        icon: 'reply'
+        href: '#'
+      }
+
+    actions
+
+  @perform: (articleContainer, type, ticket, article, ui) ->
+    return true if type isnt 'twitterStatusReply' && type isnt 'twitterDirectMessageReply'
+
+    if type is 'twitterStatusReply'
+      @twitterStatusReply(ticket, article, ui)
+
+    else if type is 'twitterDirectMessageReply'
+      @twitterDirectMessageReply(ticket, article, ui)
+
+    true
+
+  @twitterStatusReply: (ticket, article, ui) ->
+
+    ui.scrollToCompose()
+
+    # get reference article
+    type = App.TicketArticleType.find(article.type_id)
+
+    # empty form
+    articleNew = {
+      to:          ''
+      cc:          ''
+      body:        ''
+      in_reply_to: ''
+    }
+
+    if article.message_id
+      articleNew.in_reply_to = article.message_id
+
+    # get current body
+    body = ui.el.closest('.ticketZoom').find('.article-add [data-name="body"]').html().trim() || ''
+    articleNew.body = body
+
+    recipients = article.from
+    if article.to
+      if recipients
+        recipients += ', '
+      recipients += article.to
+
+    if recipients
+      recipientString = ''
+      recipientScreenNames = recipients.split(',')
+      for recipientScreenName in recipientScreenNames
+        if recipientScreenName
+          recipientScreenName = recipientScreenName.trim().toLowerCase()
+
+          # exclude already listed screen name
+          exclude = false
+          if body && body.toLowerCase().match(recipientScreenName)
+            exclude = true
+
+          # exclude own screen_name
+          if recipientScreenName is "@#{@ticket.preferences.channel_screen_name}".toLowerCase()
+            exclude = true
+
+          if exclude is false
+            if recipientString isnt ''
+              recipientString += ' '
+            recipientString += recipientScreenName
+
+    if body
+      articleNew.body = "#{recipientString} #{body}&nbsp;"
+    else
+      articleNew.body = "#{recipientString}&nbsp;"
+
+    App.Event.trigger('ui::ticket::setArticleType', {
+      ticket: ticket
+      type: type
+      article: articleNew
+      position: 'end'
+    })
+
+  @twitterDirectMessageReply: (ticket, article, ui) ->
+
+    # get reference article
+    type       = App.TicketArticleType.find(article.type_id)
+    sender     = App.TicketArticleSender.find(article.sender_id)
+    customer   = App.User.find(article.created_by_id)
+
+    ui.scrollToCompose()
+
+    # empty form
+    articleNew = {
+      to:          ''
+      cc:          ''
+      body:        ''
+      in_reply_to: ''
+    }
+
+    if article.message_id
+      articleNew.in_reply_to = article.message_id
+
+    if sender.name is 'Agent'
+      articleNew.to = article.to
+    else
+      articleNew.to = article.from
+
+    if !articleNew.to
+      articleNew.to = customer.accounts['twitter'].username || customer.accounts['twitter'].uid
+
+    App.Event.trigger('ui::ticket::setArticleType', {
+      ticket: ticket
+      type: type
+      article: articleNew
+    })
+
+App.Config.set('300-TwitterReply', TwitterReply, 'TicketZoomArticleAction')

+ 21 - 369
app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee

@@ -1,21 +1,13 @@
 class App.TicketZoomArticleActions extends App.Controller
   events:
-    'click [data-type=public]':                       'publicInternal'
-    'click [data-type=internal]':                     'publicInternal'
-    'click [data-type=emailReply]':                   'emailReply'
-    'click [data-type=emailReplyAll]':                'emailReplyAll'
-    'click [data-type=twitterStatusReply]':           'twitterStatusReply'
-    'click [data-type=twitterDirectMessageReply]':    'twitterDirectMessageReply'
-    'click [data-type=facebookFeedReply]':            'facebookFeedReply'
-    'click [data-type=telegramPersonalMessageReply]': 'telegramPersonalMessageReply'
-    'click [data-type=delete]':                       'delete'
+    'click .js-ArticleAction': 'actionPerform'
 
   constructor: ->
     super
     @render()
 
   render: ->
-    actions = @actionRow(@article)
+    actions = @actionRow(@ticket, @article)
 
     if actions
       @html App.view('ticket_zoom/article_view_actions')(
@@ -25,371 +17,31 @@ class App.TicketZoomArticleActions extends App.Controller
     else
       @html ''
 
-  publicInternal: (e) =>
-    e.preventDefault()
-    articleContainer = $(e.target).closest('.ticket-article-item')
-    article_id = $(e.target).parents('[data-id]').data('id')
-
-    # storage update
-    article = App.TicketArticle.find(article_id)
-    internal = true
-    if article.internal == true
-      internal = false
-    @lastAttributres.internal = internal
-    article.updateAttributes(internal: internal)
-
-    # runntime update
-    if internal
-      articleContainer.addClass('is-internal')
-    else
-      articleContainer.removeClass('is-internal')
-
-    @render()
-
-  actionRow: (article) ->
-    if @permissionCheck('ticket.customer')
-      return []
-
+  actionRow: (ticket, article) ->
+    actionConfig = App.Config.get('TicketZoomArticleAction')
+    keys = _.keys(actionConfig).sort()
     actions = []
-    if article.internal is true
-      actions = [
-        {
-          name: 'set to public'
-          type: 'public'
-          icon: 'lock-open'
-        }
-      ]
-    else
-      actions = [
-        {
-          name: 'set to internal'
-          type: 'internal'
-          icon: 'lock'
-        }
-      ]
-    #if @article.type.name is 'note'
-    #     actions.push []
-    group = @ticket.group
-    if group.email_address_id && (article.type.name is 'email' || article.type.name is 'web')
-      actions.push {
-        name: 'reply'
-        type: 'emailReply'
-        icon: 'reply'
-        href: '#'
-      }
-      recipients = []
-      if article.sender.name is 'Customer'
-        if article.from
-          localRecipients = emailAddresses.parseAddressList(article.from)
-          if localRecipients
-            recipients = recipients.concat localRecipients
-      if article.to
-        localRecipients = emailAddresses.parseAddressList(article.to)
-        if localRecipients
-          recipients = recipients.concat localRecipients
-      if article.cc
-        localRecipients = emailAddresses.parseAddressList(article.cc)
-        if localRecipients
-          recipients = recipients.concat localRecipients
-
-      # remove system addresses
-      localAddresses = App.EmailAddress.all()
-      forgeinRecipients = []
-      recipientUsed = {}
-      for recipient in recipients
-        if !_.isEmpty(recipient.address)
-          localRecipientAddress = recipient.address.toString().toLowerCase()
-          if !recipientUsed[localRecipientAddress]
-            recipientUsed[localRecipientAddress] = true
-            localAddress = false
-            for address in localAddresses
-              if localRecipientAddress is address.email.toString().toLowerCase()
-                recipientUsed[localRecipientAddress] = true
-                localAddress = true
-            if !localAddress
-              forgeinRecipients.push recipient
-
-      # check if reply all is neede
-      if forgeinRecipients.length > 1
-        actions.push {
-          name: 'reply all'
-          type: 'emailReplyAll'
-          icon: 'reply-all'
-          href: '#'
-        }
-    if article.sender.name is 'Customer' && article.type.name is 'phone'
-      actions.push {
-        name: 'reply'
-        type: 'emailReply'
-        icon: 'reply'
-        href: '#'
-      }
-    if article.sender.name is 'Agent' && article.type.name is 'phone'
-      actions.push {
-        name: 'reply'
-        type: 'emailReply'
-        icon: 'reply'
-        href: '#'
-      }
-    if article.type.name is 'twitter status'
-      actions.push {
-        name: 'reply'
-        type: 'twitterStatusReply'
-        icon: 'reply'
-        href: '#'
-      }
-    if article.type.name is 'twitter direct-message'
-      actions.push {
-        name: 'reply'
-        type: 'twitterDirectMessageReply'
-        icon: 'reply'
-        href: '#'
-      }
-    if article.type.name is 'facebook feed post' || article.type.name is 'facebook feed comment'
-      actions.push {
-        name: 'reply'
-        type: 'facebookFeedReply'
-        icon: 'reply'
-        href: '#'
-      }
-    if article.sender.name is 'Customer' && article.type.name is 'telegram personal-message'
-      actions.push {
-        name: 'reply'
-        type: 'telegramPersonalMessageReply'
-        icon: 'reply'
-        href: '#'
-      }
-
-    actions.push {
-      name: 'split'
-      type: 'split'
-      icon: 'split'
-      href: '#ticket/create/' + article.ticket_id + '/' + article.id
-    }
-
-    if article.type.name is 'note'
-      user = undefined
-      if App.Session.get('id') == article.created_by_id
-        user = App.User.find(App.Session.get('id'))
-        if user.permission('ticket.agent')
-          actions.push {
-            name: 'delete'
-            type: 'delete'
-            icon: 'trash'
-            href: '#'
-          }
+    for key in keys
+      config = actionConfig[key]
+      if config
+        actions = config.action(actions, ticket, article, @)
     actions
 
-  facebookFeedReply: (e) =>
-    e.preventDefault()
-
-    type = App.TicketArticleType.findByAttribute('name', 'facebook feed comment')
-    @scrollToCompose()
-
-    # empty form
-    articleNew = {
-      to:          ''
-      cc:          ''
-      body:        ''
-      in_reply_to: ''
-    }
-
-    App.Event.trigger('ui::ticket::setArticleType', { ticket: @ticket, type: type, article: articleNew } )
-
-  twitterStatusReply: (e) =>
-    e.preventDefault()
-
-    # get reference article
-    article_id = $(e.target).parents('[data-id]').data('id')
-    article    = App.TicketArticle.fullLocal(article_id)
-    sender     = App.TicketArticleSender.find(article.sender_id)
-    type       = App.TicketArticleType.find(article.type_id)
-    customer   = App.User.find(article.created_by_id)
-
-    @scrollToCompose()
-
-    # empty form
-    articleNew = {
-      to:          ''
-      cc:          ''
-      body:        ''
-      in_reply_to: ''
-    }
-
-    if article.message_id
-      articleNew.in_reply_to = article.message_id
-
-    # get current body
-    body = @el.closest('.ticketZoom').find('.article-add [data-name="body"]').html().trim() || ''
-    articleNew.body = body
-
-    recipients = article.from
-    if article.to
-      if recipients
-        recipients += ', '
-      recipients += article.to
-
-    if recipients
-      recipientString = ''
-      recipientScreenNames = recipients.split(',')
-      for recipientScreenName in recipientScreenNames
-        if recipientScreenName
-          recipientScreenName = recipientScreenName.trim().toLowerCase()
-
-          # exclude already listed screen name
-          exclude = false
-          if body && body.toLowerCase().match(recipientScreenName)
-            exclude = true
-
-          # exclude own screen_name
-          if recipientScreenName is "@#{@ticket.preferences.channel_screen_name}".toLowerCase()
-            exclude = true
-
-          if exclude is false
-            if recipientString isnt ''
-              recipientString += ' '
-            recipientString += recipientScreenName
-
-    if body
-      articleNew.body = "#{recipientString} #{body}&nbsp;"
-    else
-      articleNew.body = "#{recipientString}&nbsp;"
-
-    App.Event.trigger('ui::ticket::setArticleType', { ticket: @ticket, type: type, article: articleNew, position: 'end' } )
-
-  twitterDirectMessageReply: (e) =>
+  actionPerform: (e) =>
     e.preventDefault()
 
-    # get reference article
-    article_id = $(e.target).parents('[data-id]').data('id')
-    article    = App.TicketArticle.fullLocal(article_id)
-    type       = App.TicketArticleType.find(article.type_id)
-    sender     = App.TicketArticleSender.find(article.sender_id)
-    customer   = App.User.find(article.created_by_id)
-
-    @scrollToCompose()
-
-    # empty form
-    articleNew = {
-      to:          ''
-      cc:          ''
-      body:        ''
-      in_reply_to: ''
-    }
-
-    if article.message_id
-      articleNew.in_reply_to = article.message_id
-
-    if sender.name is 'Agent'
-      articleNew.to = article.to
-    else
-      articleNew.to = article.from
-
-    if !articleNew.to
-      articleNew.to = customer.accounts['twitter'].username || customer.accounts['twitter'].uid
-
-    App.Event.trigger('ui::ticket::setArticleType', { ticket: @ticket, type: type, article: articleNew } )
-
-  emailReplyAll: (e) =>
-    @emailReply(e, true)
-
-  emailReply: (e, all = false) =>
-    e.preventDefault()
-
-    # get reference article
-    article_id         = $(e.target).parents('[data-id]').data('id')
-    article            = App.TicketArticle.fullLocal(article_id)
-    ticket             = App.Ticket.fullLocal(article.ticket_id)
-    type               = App.TicketArticleType.find(article.type_id)
-    article_created_by = App.User.find(article.created_by_id)
-    email_addresses    = App.EmailAddress.all()
-
-    @scrollToCompose()
-
-    # empty form
-    articleNew = App.Utils.getRecipientArticle(ticket, article, article_created_by, type, email_addresses, all)
-
-    # get current body
-    body = @el.closest('.ticketZoom').find('.article-add [data-name="body"]').html() || ''
-
-    # check if quote need to be added
-    signaturePosition = 'bottom'
-    selected = App.ClipBoard.getSelected('html')
-    if selected
-      selected = App.Utils.htmlCleanup(selected).html()
-    if !selected
-      selected = App.ClipBoard.getSelected('text')
-      if selected
-        selected = App.Utils.textCleanup(selected)
-        selected = App.Utils.text2html(selected)
-
-    # full quote, if needed
-    if !selected && article && App.Config.get('ui_ticket_zoom_article_email_full_quote')
-      signaturePosition = 'top'
-      if article.content_type.match('html')
-        selected = App.Utils.textCleanup(article.body)
-      if article.content_type.match('plain')
-        selected = App.Utils.textCleanup(article.body)
-        selected = App.Utils.text2html(selected)
-
-    if selected
-      selected = "<div><br><br/></div><div><blockquote type=\"cite\">#{selected}</blockquote></div><div><br></div>"
-
-      # add selected text to body
-      body = selected + body
-
-    articleNew.body = body
-
-    type = App.TicketArticleType.findByAttribute(name:'email')
-
-    App.Event.trigger('ui::ticket::setArticleType', {
-      ticket: @ticket
-      type: type
-      article: articleNew
-      signaturePosition: signaturePosition
-    })
-
-  telegramPersonalMessageReply: (e) =>
-    e.preventDefault()
-
-    # get reference article
-    article_id = $(e.target).parents('[data-id]').data('id')
-    article    = App.TicketArticle.fullLocal(article_id)
-    sender     = App.TicketArticleSender.find(article.sender_id)
-    type       = App.TicketArticleType.find(article.type_id)
-    customer   = App.User.find(article.created_by_id)
-
-    @scrollToCompose()
-
-    # empty form
-    articleNew = {
-      to:          ''
-      cc:          ''
-      body:        ''
-      in_reply_to: ''
-    }
-
-    if article.message_id
-      articleNew.in_reply_to = article.message_id
-
-    # get current body
-    articleNew.body = @el.closest('.ticketZoom').find('.article-add [data-name="body"]').html().trim() || ''
-
-    App.Event.trigger('ui::ticket::setArticleType', { ticket: @ticket, type: type, article: articleNew, position: 'end' } )
-
-  delete: (e) =>
-    e.preventDefault()
-
-    callback = ->
-      article_id = $(e.target).parents('[data-id]').data('id')
-      article    = App.TicketArticle.find(article_id)
-      article.destroy()
+    articleContainer = $(e.target).closest('.ticket-article-item')
+    type = $(e.currentTarget).attr('data-type')
+    ticket = App.Ticket.fullLocal(@ticket.id)
+    article = App.TicketArticle.fullLocal(@article.id)
 
-    new App.ControllerConfirm(
-      message: 'Sure?'
-      callback: callback
-      container: @el.closest('.content')
-    )
+    actionConfig = App.Config.get('TicketZoomArticleAction')
+    keys = _.keys(actionConfig).sort()
+    actions = []
+    for key in keys
+      config = actionConfig[key]
+      if config
+        return if !config.perform(articleContainer, type, ticket, article, @)
 
   scrollToCompose: =>
     @el.closest('.content').find('.article-add').ScrollTo()

+ 2 - 2
app/assets/javascripts/app/views/ticket_zoom/article_view_actions.jst.eco

@@ -1,7 +1,7 @@
 <div class="article-content article-actions horizontal stretch">
   <% for action in @actions: %>
-    <a href="<%= action.href %>" data-type="<%= action.type %>" class="article-action u-clickable<% if action.class: %> <%= action.class %><% end %>">
-      <%- @Icon(action.icon, 'article-action-icon') %><%- @T( action.name ) %>
+    <a href="<%= action.href %>" data-type="<%= action.type %>" class="article-action js-ArticleAction u-clickable<% if action.class: %> <%= action.class %><% end %>">
+      <%- @Icon(action.icon, 'article-action-icon') %><%- @T(action.name) %>
     </a>
   <% end %>
 </div>