Browse Source

Fixes #2454 - Missing validation for trigger.notification.* at Model/API/REST level.

Martin Edenhofer 6 years ago
parent
commit
295e67cb6f

+ 74 - 9
app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee

@@ -7,6 +7,9 @@ class App.UiElement.ticket_perform_action
       ticket:
         name: 'Ticket'
         model: 'Ticket'
+      article:
+        name: 'Article'
+        model: 'Article'
 
     if attribute.notification
       groups.notification =
@@ -17,8 +20,11 @@ class App.UiElement.ticket_perform_action
     elements = {}
     for groupKey, groupMeta of groups
       if !groupMeta.model || !App[groupMeta.model]
-        elements["#{groupKey}.email"] = { name: 'email', display: 'Email' }
-        elements["#{groupKey}.sms"] = { name: 'sms', display: 'SMS' }
+        if groupKey is 'notification'
+          elements["#{groupKey}.email"] = { name: 'email', display: 'Email' }
+          elements["#{groupKey}.sms"] = { name: 'sms', display: 'SMS' }
+        else if groupKey is 'article'
+          elements["#{groupKey}.note"] = { name: 'note', display: 'Notiz' }
       else
 
         for row in App[groupMeta.model].configure_attributes
@@ -163,19 +169,26 @@ class App.UiElement.ticket_perform_action
       elementRow.find('.js-attributeSelector select').val(groupAndAttribute)
 
     notificationTypeMatch = groupAndAttribute.match(/^notification.([\w]+)$/)
+    articleTypeMatch = groupAndAttribute.match(/^article.([\w]+)$/)
 
     if _.isArray(notificationTypeMatch) && notificationType = notificationTypeMatch[1]
-      elementRow.find('.js-setAttribute').html('')
-      @buildRecipientList(notificationType, elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
+      elementRow.find('.js-setAttribute').html('').addClass('hide')
+      elementRow.find('.js-setArticle').html('').addClass('hide')
+      @buildNotificationArea(notificationType, elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
+    else if _.isArray(articleTypeMatch) && articleType = articleTypeMatch[1]
+      elementRow.find('.js-setAttribute').html('').addClass('hide')
+      elementRow.find('.js-setNotification').html('').addClass('hide')
+      @buildArticleArea(articleType, elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
     else
-      elementRow.find('.js-setNotification').html('')
+      elementRow.find('.js-setNotification').html('').addClass('hide')
+      elementRow.find('.js-setArticle').html('').addClass('hide')
       if !elementRow.find('.js-setAttribute div').get(0)
         attributeSelectorElement = $( App.view('generic/ticket_perform_action/attribute_selector')(
           attribute: attribute
           name: name
           meta: meta || {}
         ))
-        elementRow.find('.js-setAttribute').html(attributeSelectorElement)
+        elementRow.find('.js-setAttribute').html(attributeSelectorElement).removeClass('hide')
       @buildOperator(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
 
   @buildOperator: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
@@ -307,7 +320,7 @@ class App.UiElement.ticket_perform_action
 
     elementRow.find('.js-value').removeClass('hide').html(item)
 
-  @buildRecipientList: (notificationType, elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
+  @buildNotificationArea: (notificationType, elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
 
     return if elementRow.find(".js-setNotification .js-body-#{notificationType}").get(0)
 
@@ -344,7 +357,7 @@ class App.UiElement.ticket_perform_action
 
     selection = column_select.element()
 
-    notificationElement = $( App.view('generic/ticket_perform_action/notification_email')(
+    notificationElement = $( App.view('generic/ticket_perform_action/notification')(
       attribute: attribute
       name: name
       notificationType: notificationType
@@ -377,7 +390,59 @@ class App.UiElement.ticket_perform_action
       ]
     )
 
-    elementRow.find('.js-setNotification').html(notificationElement)
+    elementRow.find('.js-setNotification').html(notificationElement).removeClass('hide')
+
+  @buildArticleArea: (articleType, elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
+
+    return if elementRow.find(".js-setArticle .js-body-#{articleType}").get(0)
+
+    elementRow.find('.js-setArticle').empty()
+
+    name = "#{attribute.name}::article.#{articleType}"
+    console.log('meta', meta)
+    selection = App.UiElement.select.render(
+      name: "#{name}::internal"
+      multiple: false
+      null: false
+      options: { true: 'internal', false: 'public' }
+      value: meta.internal
+      class: 'form-control--small'
+      translate: true
+    )
+    articleElement = $( App.view('generic/ticket_perform_action/article')(
+      attribute: attribute
+      name: name
+      articleType: articleType
+      meta: meta || {}
+    ))
+    articleElement.find('.js-internal').html(selection)
+    articleElement.find('.js-body div[contenteditable="true"]').ce(
+      mode: 'richtext'
+      placeholder: 'message'
+      maxlength: 200000
+    )
+    new App.WidgetPlaceholder(
+      el: articleElement.find('.js-body div[contenteditable="true"]').parent()
+      objects: [
+        {
+          prefix: 'ticket'
+          object: 'Ticket'
+          display: 'Ticket'
+        },
+        {
+          prefix: 'article'
+          object: 'TicketArticle'
+          display: 'Article'
+        },
+        {
+          prefix: 'user'
+          object: 'User'
+          display: 'Current User'
+        },
+      ]
+    )
+
+    elementRow.find('.js-setArticle').html(articleElement).removeClass('hide')
 
   @humanText: (condition) ->
     none = App.i18n.translateContent('No filter.')

+ 1 - 1
app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee

@@ -180,7 +180,7 @@ class ArticleViewItem extends App.ObserverController
         links:       links
       )
       return
-    if article.sender.name is 'System'
+    if article.sender.name is 'System' && article.type.name isnt 'note'
     #if article.sender.name is 'System' && article.preferences.perform_origin is 'trigger'
       @html App.view('ticket_zoom/article_view_system')(
         ticket:      @ticket

+ 5 - 0
app/assets/javascripts/app/views/generic/ticket_perform_action/article.jst.eco

@@ -0,0 +1,5 @@
+<div class="flex">
+  <div class="js-internal"></div>
+  <div class="controls js-subject"><input type="text" name="<%= @name %>::subject" value="<%= @meta.subject %>" style="width: 100%;" placeholder="<%- @T('Subject') %>"></div>
+  <div class="controls js-body js-body-<%- @articleType %>"><div class="richtext form-control"><div contenteditable="true" data-name="<%= @name %>::body"><%- @meta.body %></div></div></div>
+</div>

+ 0 - 0
app/assets/javascripts/app/views/generic/ticket_perform_action/notification_email.jst.eco → app/assets/javascripts/app/views/generic/ticket_perform_action/notification.jst.eco


+ 1 - 0
app/assets/javascripts/app/views/generic/ticket_perform_action/row.jst.eco

@@ -7,6 +7,7 @@
     </div>
     <div class="js-setAttribute"></div>
     <div class="js-setNotification flex"></div>
+    <div class="js-setArticle flex"></div>
   </div>
   <div class="filter-controls">
     <div class="filter-control filter-control-remove js-remove" title="<%- @Ti('Remote') %>">

+ 30 - 0
app/models/concerns/checks_perform_validation.rb

@@ -0,0 +1,30 @@
+# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
+module ChecksPerformValidation
+  extend ActiveSupport::Concern
+
+  included do
+    before_create :validate_perform
+    before_update :validate_perform
+  end
+
+  def validate_perform
+    # use Marshal to do a deep copy of the perform hash
+    validate_perform = Marshal.load(Marshal.dump(perform))
+
+    check_present = {
+      'article.note'       => %w[body subject internal],
+      'notification.email' => %w[body recipient subject],
+      'notification.sms'   => %w[body recipient],
+    }
+
+    check_present.each do |key, values|
+      next if validate_perform[key].blank?
+
+      values.each do |value|
+        raise Exceptions::UnprocessableEntity, "Invalid perform #{key}, #{value} is missing!" if validate_perform[key][value].blank?
+      end
+    end
+
+    true
+  end
+end

+ 1 - 0
app/models/job.rb

@@ -3,6 +3,7 @@
 class Job < ApplicationModel
   include ChecksClientNotification
   include ChecksConditionValidation
+  include ChecksPerformValidation
 
   include Job::Assets
 

+ 26 - 2
app/models/ticket.rb

@@ -846,12 +846,17 @@ perform changes on ticket
     end
 
     perform_notification = {}
+    perform_article = {}
     changed = false
     perform.each do |key, value|
       (object_name, attribute) = key.split('.', 2)
-      raise "Unable to update object #{object_name}.#{attribute}, only can update tickets and send notifications!" if object_name != 'ticket' && object_name != 'notification'
+      raise "Unable to update object #{object_name}.#{attribute}, only can update tickets, send notifications and create articles!" if object_name != 'ticket' && object_name != 'article' && object_name != 'notification'
 
-      # send notification (after changes are done)
+      # send notification/create article (after changes are done)
+      if object_name == 'article'
+        perform_article[key] = value
+        next
+      end
       if object_name == 'notification'
         perform_notification[key] = value
         next
@@ -910,6 +915,25 @@ perform changes on ticket
       save!
     end
 
+    perform_article.each do |key, value|
+      raise 'Unable to create article, we only support article.note' if key != 'article.note'
+
+      Ticket::Article.create!(
+        ticket_id:     id,
+        subject:       value[:subject],
+        content_type:  'text/html',
+        body:          value[:body],
+        internal:      value[:internal],
+        sender:        Ticket::Article::Sender.find_by(name: 'System'),
+        type:          Ticket::Article::Type.find_by(name: 'note'),
+        preferences:   {
+          perform_origin: perform_origin,
+        },
+        updated_by_id: 1,
+        created_by_id: 1,
+      )
+    end
+
     perform_notification.each do |key, value|
 
       # send notification

+ 1 - 0
app/models/trigger.rb

@@ -2,6 +2,7 @@
 
 class Trigger < ApplicationModel
   include ChecksConditionValidation
+  include ChecksPerformValidation
   include CanSeed
 
   include Trigger::Assets

+ 1 - 1
spec/db/migrate/issue_2019_fix_double_domain_links_in_trigger_emails_spec.rb

@@ -1,7 +1,7 @@
 require 'rails_helper'
 
 RSpec.describe Issue2019FixDoubleDomainLinksInTriggerEmails, type: :db_migration do
-  subject { create(:trigger, perform: { 'notification.email' => { 'body' => faulty_link } }) }
+  subject { create(:trigger, perform: { 'notification.email' => { 'body' => faulty_link, 'recipient' => 'customer', 'subject' => 'some subject' } }) }
 
   let(:faulty_link) do
     '<a href="https://example.com/#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}">' \

Some files were not shown because too many files changed in this diff