diff --git a/.pkgr.yml b/.pkgr.yml index 58f0cb2fc..a61832909 100644 --- a/.pkgr.yml +++ b/.pkgr.yml @@ -3,10 +3,6 @@ description: Zammad is a web based open source helpdesk/customer support system homepage: https://zammad.org notifications: false targets: - centos-6: - dependencies: - - nginx - - postgresql-server centos-7: dependencies: - nginx diff --git a/app/assets/javascripts/app/controllers/_application_controller_form.coffee b/app/assets/javascripts/app/controllers/_application_controller_form.coffee index bba8696a8..3f5859a30 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_form.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_form.coffee @@ -435,7 +435,7 @@ class App.ControllerForm extends App.Controller else value = parseInt(value) if param[item.name] isnt undefined - if typeof param[item.name] is 'string' + if typeof param[item.name] is 'string' || typeof param[item.name] is 'boolean' || typeof param[item.name] is 'number' param[item.name] = [param[item.name], value] else param[item.name].push value diff --git a/app/assets/javascripts/app/controllers/_integration/sipgate_io.coffee b/app/assets/javascripts/app/controllers/_integration/sipgate_io.coffee index db8a71ede..060e0c2d2 100644 --- a/app/assets/javascripts/app/controllers/_integration/sipgate_io.coffee +++ b/app/assets/javascripts/app/controllers/_integration/sipgate_io.coffee @@ -6,6 +6,9 @@ class Index extends App.ControllerIntegrationBase ['This service shows you contacts of incoming calls and a caller list in realtime.'] ['Also caller id of outbound calls can be changed.'] ] + events: + 'click .js-select': 'selectAll' + 'change .js-switch input': 'switch' render: => super diff --git a/app/assets/javascripts/app/controllers/_settings/area.coffee b/app/assets/javascripts/app/controllers/_settings/area.coffee index 7cb2e6bfe..48215e145 100644 --- a/app/assets/javascripts/app/controllers/_settings/area.coffee +++ b/app/assets/javascripts/app/controllers/_settings/area.coffee @@ -35,10 +35,11 @@ class App.SettingsArea extends App.Controller elements = [] for setting in settings - if setting.preferences.controller && App[setting.preferences.controller] - item = new App[setting.preferences.controller](setting: setting) - else - item = new App.SettingsAreaItem(setting: setting) - elements.push item.el + if setting.preferences.hidden isnt true + if setting.preferences.controller && App[setting.preferences.controller] + item = new App[setting.preferences.controller](setting: setting) + else + item = new App.SettingsAreaItem(setting: setting) + elements.push item.el @html elements diff --git a/app/assets/javascripts/app/controllers/_settings/area_item.coffee b/app/assets/javascripts/app/controllers/_settings/area_item.coffee index 2f54d0c3b..1685cc292 100644 --- a/app/assets/javascripts/app/controllers/_settings/area_item.coffee +++ b/app/assets/javascripts/app/controllers/_settings/area_item.coffee @@ -1,4 +1,5 @@ class App.SettingsAreaItem extends App.Controller + template: 'settings/item' events: 'submit form': 'update' @@ -21,8 +22,12 @@ class App.SettingsAreaItem extends App.Controller # form @configure_attributes = @setting.options['form'] + for attribute in @configure_attributes + if attribute.tag is 'boolean' + attribute.translate = true + # item - @html App.view('settings/item')( + @html App.view(@template)( setting: @setting ) diff --git a/app/assets/javascripts/app/controllers/_settings/area_storage_provider.coffee b/app/assets/javascripts/app/controllers/_settings/area_storage_provider.coffee new file mode 100644 index 000000000..cd49fefd9 --- /dev/null +++ b/app/assets/javascripts/app/controllers/_settings/area_storage_provider.coffee @@ -0,0 +1,2 @@ +class App.SettingsAreaStorageProvider extends App.SettingsAreaItem + template: 'settings/storage_provider' diff --git a/app/assets/javascripts/app/controllers/_settings/area_ticket_hook_position.coffee b/app/assets/javascripts/app/controllers/_settings/area_ticket_hook_position.coffee new file mode 100644 index 000000000..10d00b739 --- /dev/null +++ b/app/assets/javascripts/app/controllers/_settings/area_ticket_hook_position.coffee @@ -0,0 +1,2 @@ +class App.SettingsAreaTicketHookPosition extends App.SettingsAreaItem + template: 'settings/ticket_hook_position' diff --git a/app/assets/javascripts/app/controllers/_settings/area_ticket_number.coffee b/app/assets/javascripts/app/controllers/_settings/area_ticket_number.coffee new file mode 100644 index 000000000..70ccfb563 --- /dev/null +++ b/app/assets/javascripts/app/controllers/_settings/area_ticket_number.coffee @@ -0,0 +1,132 @@ +class App.SettingsAreaTicketNumber extends App.Controller + events: + 'submit form': 'update' + + constructor: -> + super + @render() + + render: => + + # defaults + directValue = 0 + for item in @setting.options['form'] + directValue += 1 + if directValue > 1 + for item in @setting.options['form'] + item['default'] = @setting.state_current.value[item.name] + else + item['default'] = @setting.state_current.value + + @map = + 'Ticket::Number::Increment': 'ticket_number_increment' + 'Ticket::Number::Date': 'ticket_number_date' + + # form + @configure_attributes = @setting.options['form'] + + # item + @html App.view('settings/ticket_number')( + setting: @setting + ) + + togglePreferences = (params, attribute, @attributes, classname, form) => + return if attribute.name isnt 'ticket_number' + @showPreferences(params.ticket_number) + + updatePreview = (params, attribute) => + paramsParent = @formParam(@$('.js-form')) + number = "#{App.Config.get('ticket_hook')}???" + if paramsParent.ticket_number is 'Ticket::Number::Increment' + paramsItem = @paramsPreferences('Ticket::Number::Increment') + number = "#{App.Config.get('ticket_hook')}#{App.Config.get('system_id')}" + counter = '1' + if paramsItem.min_size + minSize = parseInt(paramsItem.min_size) + if paramsItem.checksum + minSize -= 1 + if minSize > 1 + for itemCounter in [2 .. minSize] + counter = "0#{counter}" + number += counter + if paramsItem.checksum + number += '9' + else if paramsParent.ticket_number is 'Ticket::Number::Date' + paramsItem = @paramsPreferences('Ticket::Number::Date') + current = new Date() + currentDay = current.getDate() + currentMonth = current.getMonth() + 1 + currentYear = current.getFullYear() + + number = "#{App.Config.get('ticket_hook')}#{currentYear}#{currentMonth}#{currentDay}#{App.Config.get('system_id')}001" + if paramsItem.checksum + number += '9' + + @$('.js-preview').text(number) + + new App.ControllerForm( + el: @el.find('.js-form'), + model: { configure_attributes: @configure_attributes, className: '' } + autofocus: false + handlers: [togglePreferences, updatePreview] + ) + + # preferences + preferences_settings = @setting.preferences.settings_included || ['ticket_number_increment', 'ticket_number_date'] + for preferences_setting in preferences_settings + setting = App.Setting.findByAttribute('name', preferences_setting) + value = App.Setting.get(preferences_setting) + el = $(App.view("settings/#{preferences_setting}")( + setting: setting + )) + new App.ControllerForm( + el: el.find('.js-formItem'), + model: { configure_attributes: setting.options['form'], className: '' } + autofocus: false + params: value + handlers: [updatePreview] + ) + @$('.js-formPreferences').append(el) + + # show current preferences + @showPreferences(item['default']) + + showPreferences: (name) => + @$('.js-formPreferencesItem').addClass('hidden') + @$(".js-formPreferencesItem[data-backend=\"#{name}\"]").removeClass('hidden') + + paramsPreferences: (name) => + @formParam(@$(".js-formPreferencesItem[data-backend=\"#{name}\"] form")) + + update: (e) => + e.preventDefault() + @formDisable(@$('.js-form')) + params = @formParam(@$('.js-form')) + if params.ticket_number + paramsItem = @paramsPreferences(params.ticket_number) + setting_name = @map[params.ticket_number] + if setting_name && paramsItem + App.Setting.set(setting_name, paramsItem) + + @setting['state_current'] = {value: params.ticket_number} + ui = @ + @setting.save( + done: => + ui.formEnable(e) + App.Event.trigger 'notify', { + type: 'success' + msg: App.i18n.translateContent('Update successful!') + timeout: 2000 + } + + # rerender ui || get new collections and session data + App.Setting.preferencesPost(@setting) + + fail: (settings, details) -> + ui.formEnable(e) + App.Event.trigger 'notify', { + type: 'error' + msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!') + timeout: 2000 + } + ) diff --git a/app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee b/app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee index e6092a2bf..d6bdfb02c 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee @@ -16,8 +16,11 @@ class App.UiElement.postmaster_set relation: 'TicketState' }, { - value: 'x-zammad-ticket-customer' + value: 'x-zammad-ticket-customer_id' name: 'Customer' + relation: 'User' + tag: 'user_autocompletion' + disableCreateUser: true, }, { @@ -26,10 +29,11 @@ class App.UiElement.postmaster_set relation: 'Group' }, { - value: 'x-zammad-ticket-owner' + value: 'x-zammad-ticket-owner_id' name: 'Owner' - relation: 'User', - tag: 'user_autocompletion', + relation: 'User' + tag: 'user_autocompletion' + disableCreateUser: true, }, { value: 'x-zammad-ignore' diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee index e769a72fe..26296dbd5 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee @@ -48,6 +48,7 @@ class App.TicketZoomArticleNew extends App.Controller possibleArticleType['email'] = true # gets referenced in @setArticleType + @internalSelector = true @type = @defaults['type'] || 'note' @articleTypes = [] if possibleArticleType.note @@ -115,6 +116,9 @@ class App.TicketZoomArticleNew extends App.Controller }, ] + if @permissionCheck('ticket.customer') + @internalSelector = false + @textareaHeight = open: 148 closed: 20 @@ -194,11 +198,12 @@ class App.TicketZoomArticleNew extends App.Controller ticket = App.Ticket.fullLocal(@ticket_id) @html App.view('ticket_zoom/article_new')( - ticket: ticket - articleTypes: @articleTypes - article: @defaults - form_id: @form_id - isCustomer: @permissionCheck('ticket.customer') + ticket: ticket + articleTypes: @articleTypes + article: @defaults + form_id: @form_id + isCustomer: @permissionCheck('ticket.customer') + internalSelector: @internalSelector ) @setArticleType(@type) diff --git a/app/assets/javascripts/app/lib/app_post/utils.coffee b/app/assets/javascripts/app/lib/app_post/utils.coffee index b70204783..e6cf40a36 100644 --- a/app/assets/javascripts/app/lib/app_post/utils.coffee +++ b/app/assets/javascripts/app/lib/app_post/utils.coffee @@ -206,7 +206,7 @@ class App.Utils # Replace all x tags with the type of replacementTag html.find('textarea').each( -> - outer = @outerHTML; + outer = @outerHTML # Replace opening tag regex = new RegExp('<' + @tagName, 'i') diff --git a/app/assets/javascripts/app/views/integration/sipgate.jst.eco b/app/assets/javascripts/app/views/integration/sipgate.jst.eco index 9c3f5e98a..937377a86 100644 --- a/app/assets/javascripts/app/views/integration/sipgate.jst.eco +++ b/app/assets/javascripts/app/views/integration/sipgate.jst.eco @@ -1,5 +1,27 @@
+

sipgate.io <%- @T('Settings') %>

+ +

<%- @T('You need to configure the Zammad endpoints in the Sipgate web interface') %>:

+ +

+ + + + + + + + +
<%- @T('Type') %> + <%- @T('URL') %> +
<%- @T('Inbound') %> + +
<%- @T('Outbound') %> + +
+
+

<%- @T('Inbound') %>

<%- @T('Blocked caller ids based on sender caller id.') %> diff --git a/app/assets/javascripts/app/views/login.jst.eco b/app/assets/javascripts/app/views/login.jst.eco index ff543fe8a..2f0076023 100644 --- a/app/assets/javascripts/app/views/login.jst.eco +++ b/app/assets/javascripts/app/views/login.jst.eco @@ -81,8 +81,8 @@

- <%- @Icon('logo') %> + <%- @Icon('logo') %> <%- @T('Powered by') %> - <%- @Icon('logotype', 'logotype') %> + <%- @Icon('logotype', 'logotype') %>
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/settings/item.jst.eco b/app/assets/javascripts/app/views/settings/item.jst.eco index 88a89dc98..752a44ed3 100644 --- a/app/assets/javascripts/app/views/settings/item.jst.eco +++ b/app/assets/javascripts/app/views/settings/item.jst.eco @@ -1,6 +1,6 @@

<%- @T(@setting.title) %>

-

<%- @RichText(@setting.description) %>

+

<%- @T(@setting.description) %>

diff --git a/app/assets/javascripts/app/views/settings/storage_provider.jst.eco b/app/assets/javascripts/app/views/settings/storage_provider.jst.eco new file mode 100644 index 000000000..eaddccd4c --- /dev/null +++ b/app/assets/javascripts/app/views/settings/storage_provider.jst.eco @@ -0,0 +1,22 @@ + +

<%- @T(@setting.title) %>

+

<%- @T('You can switch between the backend for new attachments even on a system that is already in production without any loss of data.') %>

+

<%- @T('If you want to move already stored attachments from one backend to another, you need to execute the following via console.') %>

+

+

<%- @T('Move all from "%s" to "%s"', 'filesystem', 'database') %>:

+

+ + rails> Store::File.move('File', 'DB') + +

<%- @T('Move all from "%s" to "%s"', 'database', 'filesystem') %>:

+

+ + rails> Store::File.move('DB', 'File') + +
+
+
+
+ +
+ diff --git a/app/assets/javascripts/app/views/settings/ticket_hook_position.jst.eco b/app/assets/javascripts/app/views/settings/ticket_hook_position.jst.eco new file mode 100644 index 000000000..be706ce60 --- /dev/null +++ b/app/assets/javascripts/app/views/settings/ticket_hook_position.jst.eco @@ -0,0 +1,13 @@ +
+

<%- @T(@setting.title) %>

+

<%- @T('The format of the subject.') %>

+
    +
  • <%- @T('|Right| means |Some Subject [Ticket#12345]|') %> +
  • <%- @T('|Left| means |[Ticket#12345] Some Subject|') %> +
  • <%- @T('|None| means |Some Subject| (without ticket number). In the last case you should enable "postmaster___follow___up___search___in" to recognize followups based on email headers and/or body.') %> +
+
+
+ +
+
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/settings/ticket_number.jst.eco b/app/assets/javascripts/app/views/settings/ticket_number.jst.eco new file mode 100644 index 000000000..6c910d11e --- /dev/null +++ b/app/assets/javascripts/app/views/settings/ticket_number.jst.eco @@ -0,0 +1,19 @@ +
+

<%- @T(@setting.title) %>

+

+ <%- @T('Selects the ticket number generator module.') %> +

    +
  • <%- @T('|Increment| increments the ticket number, the SystemID and the counter are used with "SystemID.Counter" format (e.g. 1010138, 1010139).') %> +
  • <%- @T('With |Date| the ticket numbers will be generated by the current date, the SystemID and the counter. The format looks like "Year.Month.Day.SystemID.Counter" (e.g. 201206231010138, 201206231010139).') %> +
+
+
+
+
+ +
+
+
+ +
+
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/settings/ticket_number_date.jst.eco b/app/assets/javascripts/app/views/settings/ticket_number_date.jst.eco new file mode 100644 index 000000000..aa5706164 --- /dev/null +++ b/app/assets/javascripts/app/views/settings/ticket_number_date.jst.eco @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/app/assets/javascripts/app/views/settings/ticket_number_increment.jst.eco b/app/assets/javascripts/app/views/settings/ticket_number_increment.jst.eco new file mode 100644 index 000000000..b203fbea3 --- /dev/null +++ b/app/assets/javascripts/app/views/settings/ticket_number_increment.jst.eco @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/app/assets/javascripts/app/views/ticket_zoom/article_new.jst.eco b/app/assets/javascripts/app/views/ticket_zoom/article_new.jst.eco index 122a7ebcd..becfceb2b 100644 --- a/app/assets/javascripts/app/views/ticket_zoom/article_new.jst.eco +++ b/app/assets/javascripts/app/views/ticket_zoom/article_new.jst.eco @@ -21,6 +21,7 @@ <% end %>
+ <% if @internalSelector: %> + <% end %>
diff --git a/app/models/channel/driver/smtp.rb b/app/models/channel/driver/smtp.rb index 8a05decc5..23d6d5256 100644 --- a/app/models/channel/driver/smtp.rb +++ b/app/models/channel/driver/smtp.rb @@ -49,16 +49,21 @@ class Channel::Driver::Smtp options[:openssl_verify_mode] = 'none' end mail = Channel::EmailBuild.build(attr, notification) - mail.delivery_method :smtp, { + smtp_params = { openssl_verify_mode: options[:openssl_verify_mode], address: options[:host], port: options[:port], domain: options[:domain], - user_name: options[:user], - password: options[:password], enable_starttls_auto: options[:enable_starttls_auto], - authentication: options[:authentication], } + + # add authentication only if needed + if options[:user] && !options[:user].empty? + smtp_params[:user_name] = options[:user] + smtp_params[:password] = options[:password] + smtp_params[:authentication] = options[:authentication] + end + mail.delivery_method :smtp, smtp_params mail.deliver end end diff --git a/app/models/channel/email_parser.rb b/app/models/channel/email_parser.rb index 7f80d5ede..571f64645 100644 --- a/app/models/channel/email_parser.rb +++ b/app/models/channel/email_parser.rb @@ -394,6 +394,7 @@ returns p 'ERROR: ' + e.inspect # rubocop:disable Rails/Output Rails.logger.error message Rails.logger.error 'ERROR: ' + e.inspect + Rails.logger.error 'ERROR: ' + e.backtrace.inspect File.open(filename, 'wb') { |file| file.write msg } diff --git a/app/models/channel/filter/auto_response_check.rb b/app/models/channel/filter/auto_response_check.rb index 51baadb69..c6481beca 100644 --- a/app/models/channel/filter/auto_response_check.rb +++ b/app/models/channel/filter/auto_response_check.rb @@ -15,7 +15,7 @@ module Channel::Filter::AutoResponseCheck mail[ 'x-zammad-article-preferences'.to_sym ]['is-auto-response'] = true return if mail[ 'x-loop'.to_sym ] && mail[ 'x-loop'.to_sym ] =~ /(yes|true)/i - return if mail[ 'precedence'.to_sym ] && mail[ 'precedence'.to_sym ] =~ /bulk/i + return if mail[ 'precedence'.to_sym ] && mail[ 'precedence'.to_sym ] =~ /(bulk|list|junk)/i return if mail[ 'auto-submitted'.to_sym ] && mail[ 'auto-submitted'.to_sym ] =~ /auto-(generated|replied)/i return if mail[ 'x-auto-response-suppress'.to_sym ] && mail[ 'x-auto-response-suppress'.to_sym ] =~ /all/i diff --git a/app/models/observer/transaction.rb b/app/models/observer/transaction.rb index 385263676..e62b7e788 100644 --- a/app/models/observer/transaction.rb +++ b/app/models/observer/transaction.rb @@ -29,6 +29,7 @@ class Observer::Transaction < ActiveRecord::Observer sync_backends = [] Setting.where(area: 'Transaction::Backend::Sync').order(:name).each { |setting| backend = Setting.get(setting.name) + next if params[:disable] && params[:disable].include?(backend) sync_backends.push Kernel.const_get(backend) } diff --git a/app/models/ticket.rb b/app/models/ticket.rb index 220935ae2..5ad23f19c 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -844,6 +844,22 @@ result references end +=begin + +get all articles of a ticket in correct order (overwrite active record default method) + + artilces = ticket.articles + +result + + [article1, articl2] + +=end + + def articles + Ticket::Article.where(ticket_id: id).order(:created_at, :id) + end + private def check_generate diff --git a/app/models/ticket/number.rb b/app/models/ticket/number.rb index 4dcbba8b6..6617da1b0 100644 --- a/app/models/ticket/number.rb +++ b/app/models/ticket/number.rb @@ -20,7 +20,7 @@ returns # generate number 49_999.times { number = adapter.generate - ticket = Ticket.find_by( number: number ) + ticket = Ticket.find_by(number: number) return number if !ticket } raise "Can't generate new ticket number!" diff --git a/app/models/ticket/number/date.rb b/app/models/ticket/number/date.rb index a8cddca8c..609488532 100644 --- a/app/models/ticket/number/date.rb +++ b/app/models/ticket/number/date.rb @@ -43,6 +43,14 @@ module Ticket::Number::Date # vehikel number. The modulus to 10 of this sum is substracted from # 10. See: http://www.pruefziffernberechnung.de/F/Fahrzeugnummer.shtml # (german) + + # fix for https://github.com/zammad/zammad/issues/413 - can be removed later + if config.class == FalseClass || config.class == TrueClass + config = { + checksum: config + } + end + if config[:checksum] chksum = 0 mult = 1 diff --git a/app/models/ticket/overviews.rb b/app/models/ticket/overviews.rb index 7ce24fc3c..05f502deb 100644 --- a/app/models/ticket/overviews.rb +++ b/app/models/ticket/overviews.rb @@ -90,13 +90,36 @@ returns # get only tickets with permissions access_condition = Ticket.access_condition(user) + ticket_attributes = Ticket.new.attributes list = [] overviews.each { |overview| query_condition, bind_condition, tables = Ticket.selector2sql(overview.condition, user) - order_by = "#{overview.order[:by]} #{overview.order[:direction]}" + # validate direction + raise "Invalid order direction '#{overview.order[:direction]}'" if overview.order[:direction] && overview.order[:direction] !~ /^(ASC|DESC)$/i + + # check if order by exists + order_by = overview.order[:by] + if !ticket_attributes.key?(order_by) + order_by = if ticket_attributes.key?("#{order_by}_id") + "#{order_by}_id" + else + 'created_at' + end + end + order_by = "tickets.#{order_by} #{overview.order[:direction]}" + + # check if group by exists if overview.group_by && !overview.group_by.empty? - order_by = "#{overview.group_by}_id, #{order_by}" + group_by = overview.group_by + if !ticket_attributes.key?(group_by) + group_by = if ticket_attributes.key?("#{group_by}_id") + "#{group_by}_id" + end + end + if group_by + order_by = "tickets.#{group_by}, #{order_by}" + end end ticket_result = Ticket.select('id, updated_at') @@ -115,9 +138,10 @@ returns } tickets.push ticket_item } - count = Ticket.where(access_condition).where(query_condition, *bind_condition).count() + count = Ticket.where(access_condition).where(query_condition, *bind_condition).joins(tables).count() item = { overview: { + name: overview.name, id: overview.id, view: overview.link, updated_at: overview.updated_at, diff --git a/app/models/transaction.rb b/app/models/transaction.rb index 23e1641d1..25b94fa55 100644 --- a/app/models/transaction.rb +++ b/app/models/transaction.rb @@ -13,7 +13,10 @@ class Transaction if options[:interface_handle] ApplicationHandleInfo.current = original_interface_handle end - Observer::Transaction.commit(disable_notification: options[:disable_notification]) + Observer::Transaction.commit( + disable_notification: options[:disable_notification], + disable: options[:disable], + ) PushMessages.finish end end diff --git a/app/models/transaction/background_job.rb b/app/models/transaction/background_job.rb index e8a71ef2b..57a649b1a 100644 --- a/app/models/transaction/background_job.rb +++ b/app/models/transaction/background_job.rb @@ -24,7 +24,9 @@ class Transaction::BackgroundJob def perform Setting.where(area: 'Transaction::Backend::Async').order(:name).each { |setting| - backend = Kernel.const_get(Setting.get(setting.name)) + backend = Setting.get(setting.name) + next if @params[:disable] && @params[:disable].include?(backend) + backend = Kernel.const_get(backend) Observer::Transaction.execute_singel_backend(backend, @item, @params) } end diff --git a/app/models/transaction/trigger.rb b/app/models/transaction/trigger.rb index 90fff8c26..7b2b6ad5d 100644 --- a/app/models/transaction/trigger.rb +++ b/app/models/transaction/trigger.rb @@ -29,7 +29,7 @@ class Transaction::Trigger return if @item[:object] != 'Ticket' - triggers = Trigger.where(active: true) + triggers = Trigger.where(active: true).order('LOWER(name)') return if triggers.empty? ticket = Ticket.lookup(id: @item[:object_id]) @@ -39,86 +39,94 @@ class Transaction::Trigger end original_user_id = UserInfo.current_user_id - UserInfo.current_user_id = 1 - triggers.each { |trigger| - condition = trigger.condition + Transaction.execute(reset_user_id: true, disable: ['Transaction::Trigger']) do + triggers.each { |trigger| + condition = trigger.condition - # check action - if condition['ticket.action'] - next if condition['ticket.action']['operator'] == 'is' && condition['ticket.action']['value'] != @item[:type] - next if condition['ticket.action']['operator'] != 'is' && condition['ticket.action']['value'] == @item[:type] - condition.delete('ticket.action') - end - - # check "has changed" options - has_changed_condition_exists = false - has_changed = false - condition.each do |key, value| - next if !value - next if !value['operator'] - next if !value['operator']['has changed'] - has_changed_condition_exists = true - - # next if has changed? && !@item[:changes][attribute] - (object_name, attribute) = key.split('.', 2) - - # remove condition item, because it has changed - if @item[:changes][attribute] - has_changed = true - condition.delete(key) - next + # check action + if condition['ticket.action'] + next if condition['ticket.action']['operator'] == 'is' && condition['ticket.action']['value'] != @item[:type] + next if condition['ticket.action']['operator'] != 'is' && condition['ticket.action']['value'] == @item[:type] + condition.delete('ticket.action') end - break - end - next if has_changed_condition_exists && !has_changed + # check action + if condition['article.action'] + next if !article + condition.delete('article.action') + end - # check if selector is matching - condition['ticket.id'] = { - operator: 'is', - value: ticket.id, - } - if article - condition['article.id'] = { - operator: 'is', - value: article.id, - } - end + # check "has changed" options + has_changed_condition_exists = false + has_changed = false + condition.each do |key, value| + next if !value + next if !value['operator'] + next if !value['operator']['has changed'] + has_changed_condition_exists = true - ticket_count, tickets = Ticket.selectors(condition, 1) - next if ticket_count.zero? - next if tickets.first.id != ticket.id + # next if has changed? && !@item[:changes][attribute] + (object_name, attribute) = key.split('.', 2) - # check if min one article attribute is used - article_selector = false - trigger.condition.each do |key, _value| - (object_name, attribute) = key.split('.', 2) - next if object_name != 'article' - next if attribute == 'id' - article_selector = true - end - - # check in min one attribute has changed - if @item[:type] == 'update' && !article_selector - match = false - if has_changed_condition_exists && has_changed - match = true - else - trigger.condition.each do |key, _value| - (object_name, attribute) = key.split('.', 2) - next if object_name != 'ticket' - next if !@item[:changes][attribute] - match = true - break + # remove condition item, because it has changed + if @item[:changes][attribute] + has_changed = true + condition.delete(key) + next end + break end - next if !match - end - Transaction.execute do + + next if has_changed_condition_exists && !has_changed + + # check if selector is matching + condition['ticket.id'] = { + operator: 'is', + value: ticket.id, + } + + # check if min one article attribute is used + article_selector = false + trigger.condition.each do |key, _value| + (object_name, attribute) = key.split('.', 2) + next if object_name != 'article' + next if attribute == 'id' + article_selector = true + end + + next if article_selector && !article + if article_selector + condition['article.id'] = { + operator: 'is', + value: article.id, + } + end + + ticket_count, tickets = Ticket.selectors(condition, 1) + next if ticket_count.zero? + next if tickets.first.id != ticket.id + + # check in min one attribute has changed + if @item[:type] == 'update' && !article_selector + match = false + if has_changed_condition_exists && has_changed + match = true + else + trigger.condition.each do |key, _value| + (object_name, attribute) = key.split('.', 2) + next if object_name != 'ticket' + next if !@item[:changes][attribute] + match = true + break + end + end + next if !match + end + ticket.perform_changes(trigger.perform, 'trigger', @item) - end - } + } + end UserInfo.current_user_id = original_user_id end diff --git a/contrib/apache2/zammad.conf b/contrib/apache2/zammad.conf index 7926d1fa9..4226b7166 100644 --- a/contrib/apache2/zammad.conf +++ b/contrib/apache2/zammad.conf @@ -4,7 +4,7 @@ # replace 'localhost' with your fqdn if you want to use zammad from remote - ServerName localhost + ServerName ubuntu.local ## don't loose time with IP address lookups HostnameLookups Off @@ -18,9 +18,8 @@ ProxyRequests Off ProxyPreserveHost On - - Order deny,allow - Allow from localhost + + Require local ProxyPass /assets ! @@ -38,8 +37,7 @@ Options FollowSymLinks - Order allow,deny - Allow from all + Require all granted diff --git a/db/migrate/20120101000001_create_base.rb b/db/migrate/20120101000001_create_base.rb index 9acd58459..125a59af3 100644 --- a/db/migrate/20120101000001_create_base.rb +++ b/db/migrate/20120101000001_create_base.rb @@ -577,7 +577,7 @@ class CreateBase < ActiveRecord::Migration t.string :key, limit: 250, null: true t.integer :related_o_id, null: true t.integer :related_stats_store_object_id, null: true - t.string :data, limit: 2500, null: true + t.string :data, limit: 5000, null: true t.integer :created_by_id, null: false t.timestamps limit: 3, null: false end diff --git a/db/migrate/20120101000010_create_ticket.rb b/db/migrate/20120101000010_create_ticket.rb index 04aee9bbe..ad4578604 100644 --- a/db/migrate/20120101000010_create_ticket.rb +++ b/db/migrate/20120101000010_create_ticket.rb @@ -186,18 +186,18 @@ class CreateTicket < ActiveRecord::Migration add_index :ticket_counters, [:generator], unique: true create_table :overviews do |t| - t.references :role, null: false - t.column :name, :string, limit: 250, null: false - t.column :link, :string, limit: 250, null: false - t.column :prio, :integer, null: false - t.column :condition, :string, limit: 2500, null: false - t.column :order, :string, limit: 2500, null: false - t.column :group_by, :string, limit: 250, null: true - t.column :organization_shared, :boolean, null: false, default: false - t.column :view, :string, limit: 1000, null: false - t.column :active, :boolean, null: false, default: true - t.column :updated_by_id, :integer, null: false - t.column :created_by_id, :integer, null: false + t.references :role, null: false + t.column :name, :string, limit: 250, null: false + t.column :link, :string, limit: 250, null: false + t.column :prio, :integer, null: false + t.column :condition, :text, limit: 500.kilobytes + 1, null: false + t.column :order, :string, limit: 2500, null: false + t.column :group_by, :string, limit: 250, null: true + t.column :organization_shared, :boolean, null: false, default: false + t.column :view, :string, limit: 1000, null: false + t.column :active, :boolean, null: false, default: true + t.column :updated_by_id, :integer, null: false + t.column :created_by_id, :integer, null: false t.timestamps limit: 3, null: false end add_index :overviews, [:name] @@ -217,34 +217,34 @@ class CreateTicket < ActiveRecord::Migration add_index :overviews_groups, [:group_id] create_table :triggers do |t| - t.column :name, :string, limit: 250, null: false - t.column :condition, :string, limit: 2500, null: false - t.column :perform, :string, limit: 2500, null: false - t.column :disable_notification, :boolean, null: false, default: true - t.column :note, :string, limit: 250, null: true - t.column :active, :boolean, null: false, default: true - t.column :updated_by_id, :integer, null: false - t.column :created_by_id, :integer, null: false + t.column :name, :string, limit: 250, null: false + t.column :condition, :text, limit: 500.kilobytes + 1, null: false + t.column :perform, :text, limit: 500.kilobytes + 1, null: false + t.column :disable_notification, :boolean, null: false, default: true + t.column :note, :string, limit: 250, null: true + t.column :active, :boolean, null: false, default: true + t.column :updated_by_id, :integer, null: false + t.column :created_by_id, :integer, null: false t.timestamps limit: 3, null: false end add_index :triggers, [:name], unique: true create_table :jobs do |t| - t.column :name, :string, limit: 250, null: false - t.column :timeplan, :string, limit: 1000, null: false - t.column :condition, :string, limit: 2500, null: false - t.column :perform, :string, limit: 2500, null: false - t.column :disable_notification, :boolean, null: false, default: true - t.column :last_run_at, :timestamp, limit: 3, null: true - t.column :next_run_at, :timestamp, limit: 3, null: true - t.column :running, :boolean, null: false, default: false - t.column :processed, :integer, null: false, default: 0 - t.column :matching, :integer, null: false - t.column :pid, :string, limit: 250, null: true - t.column :note, :string, limit: 250, null: true - t.column :active, :boolean, null: false, default: false - t.column :updated_by_id, :integer, null: false - t.column :created_by_id, :integer, null: false + t.column :name, :string, limit: 250, null: false + t.column :timeplan, :string, limit: 2500, null: false + t.column :condition, :text, limit: 500.kilobytes + 1, null: false + t.column :perform, :text, limit: 500.kilobytes + 1, null: false + t.column :disable_notification, :boolean, null: false, default: true + t.column :last_run_at, :timestamp, limit: 3, null: true + t.column :next_run_at, :timestamp, limit: 3, null: true + t.column :running, :boolean, null: false, default: false + t.column :processed, :integer, null: false, default: 0 + t.column :matching, :integer, null: false + t.column :pid, :string, limit: 250, null: true + t.column :note, :string, limit: 250, null: true + t.column :active, :boolean, null: false, default: false + t.column :updated_by_id, :integer, null: false + t.column :created_by_id, :integer, null: false t.timestamps limit: 3, null: false end add_index :jobs, [:name], unique: true @@ -285,14 +285,14 @@ class CreateTicket < ActiveRecord::Migration add_index :links, [:link_object_source_id, :link_object_source_value, :link_object_target_id, :link_object_target_value, :link_type_id], unique: true, name: 'links_uniq_total' create_table :postmaster_filters do |t| - t.column :name, :string, limit: 250, null: false - t.column :channel, :string, limit: 250, null: false - t.column :match, :string, limit: 5000, null: false - t.column :perform, :string, limit: 5000, null: false - t.column :active, :boolean, null: false, default: true - t.column :note, :string, limit: 250, null: true - t.column :updated_by_id, :integer, null: false - t.column :created_by_id, :integer, null: false + t.column :name, :string, limit: 250, null: false + t.column :channel, :string, limit: 250, null: false + t.column :match, :text, limit: 500.kilobytes + 1, null: false + t.column :perform, :text, limit: 500.kilobytes + 1, null: false + t.column :active, :boolean, null: false, default: true + t.column :note, :string, limit: 250, null: true + t.column :updated_by_id, :integer, null: false + t.column :created_by_id, :integer, null: false t.timestamps limit: 3, null: false end add_index :postmaster_filters, [:channel] @@ -359,7 +359,7 @@ class CreateTicket < ActiveRecord::Migration t.column :first_response_time, :integer, null: true t.column :update_time, :integer, null: true t.column :solution_time, :integer, null: true - t.column :condition, :string, limit: 5000, null: true + t.column :condition, :text, limit: 500.kilobytes + 1, null: true t.column :updated_by_id, :integer, null: false t.column :created_by_id, :integer, null: false t.timestamps limit: 3, null: false @@ -367,12 +367,12 @@ class CreateTicket < ActiveRecord::Migration add_index :slas, [:name], unique: true create_table :macros do |t| - t.string :name, limit: 250, null: true - t.string :perform, limit: 5000, null: false - t.boolean :active, null: false, default: true - t.string :note, limit: 250, null: true - t.integer :updated_by_id, null: false - t.integer :created_by_id, null: false + t.string :name, limit: 250, null: true + t.text :perform, limit: 500.kilobytes + 1, null: false + t.boolean :active, null: false, default: true + t.string :note, limit: 250, null: true + t.integer :updated_by_id, null: false + t.integer :created_by_id, null: false t.timestamps limit: 3, null: false end add_index :macros, [:name], unique: true @@ -436,11 +436,11 @@ class CreateTicket < ActiveRecord::Migration add_index :chat_agents, [:created_by_id], unique: true create_table :report_profiles do |t| - t.column :name, :string, limit: 150, null: true - t.column :condition, :string, limit: 6000, null: true - t.column :active, :boolean, null: false, default: true - t.column :updated_by_id, :integer, null: false - t.column :created_by_id, :integer, null: false + t.column :name, :string, limit: 150, null: true + t.column :condition, :text, limit: 500.kilobytes + 1, null: true + t.column :active, :boolean, null: false, default: true + t.column :updated_by_id, :integer, null: false + t.column :created_by_id, :integer, null: false t.timestamps limit: 3, null: false end add_index :report_profiles, [:name], unique: true diff --git a/db/migrate/20161117000001_job_unable_to_create_issue_432.rb b/db/migrate/20161117000001_job_unable_to_create_issue_432.rb new file mode 100644 index 000000000..db88c4348 --- /dev/null +++ b/db/migrate/20161117000001_job_unable_to_create_issue_432.rb @@ -0,0 +1,44 @@ +class JobUnableToCreateIssue432 < ActiveRecord::Migration + def up + # return if it's a new setup + return if !Setting.find_by(name: 'system_init_done') + + ActiveRecord::Migration.change_table :jobs do |t| + t.change :timeplan, :string, limit: 2500 + t.change :condition, :text, limit: 500.kilobytes + 1 + t.change :perform, :text, limit: 500.kilobytes + 1 + end + + ActiveRecord::Migration.change_table :triggers do |t| + t.change :condition, :text, limit: 500.kilobytes + 1 + t.change :perform, :text, limit: 500.kilobytes + 1 + end + + ActiveRecord::Migration.change_table :overviews do |t| + t.change :condition, :text, limit: 500.kilobytes + 1 + end + + ActiveRecord::Migration.change_table :report_profiles do |t| + t.change :condition, :text, limit: 500.kilobytes + 1 + end + ActiveRecord::Migration.change_table :slas do |t| + t.change :condition, :text, limit: 500.kilobytes + 1 + end + + ActiveRecord::Migration.change_table :macros do |t| + t.change :perform, :text, limit: 500.kilobytes + 1 + end + + ActiveRecord::Migration.change_table :postmaster_filters do |t| + t.change :match, :text, limit: 500.kilobytes + 1 + t.change :perform, :text, limit: 500.kilobytes + 1 + end + + ActiveRecord::Migration.change_table :stats_stores do |t| + t.change :data, :string, limit: 5000 + end + + Cache.clear + + end +end diff --git a/db/migrate/20161117000002_ticket_number_generator_issue_427.rb b/db/migrate/20161117000002_ticket_number_generator_issue_427.rb new file mode 100644 index 000000000..a0617ce04 --- /dev/null +++ b/db/migrate/20161117000002_ticket_number_generator_issue_427.rb @@ -0,0 +1,55 @@ +class TicketNumberGeneratorIssue427 < ActiveRecord::Migration + def up + # return if it's a new setup + return if !Setting.find_by(name: 'system_init_done') + + setting = Setting.find_by(name: 'ticket_number') + setting.preferences = { + settings_included: %w(ticket_number_increment ticket_number_date), + controller: 'SettingsAreaTicketNumber', + permission: ['admin.ticket'], + } + setting.save! + setting = Setting.find_by(name: 'ticket_number_increment') + setting.preferences = { + permission: ['admin.ticket'], + hidden: true, + } + setting.save! + setting = Setting.find_by(name: 'ticket_number_date') + setting.preferences = { + permission: ['admin.ticket'], + hidden: true, + } + + # just to make sure that value is saved correctly - https://github.com/zammad/zammad/issues/413 + if setting.state_current['value'] == true || setting.state_current['value'] == false + setting.state_current['value'] = { 'checksum' => setting.state_current['value'] } + end + setting.save! + + setting = Setting.find_by(name: 'ticket_hook_position') + setting.preferences = { + controller: 'SettingsAreaTicketHookPosition', + permission: ['admin.ticket'], + } + setting.options = { + form: [ + { + display: '', + null: true, + name: 'ticket_hook_position', + tag: 'select', + translate: true, + options: { + 'left' => 'left', + 'right' => 'right', + 'none' => 'none', + }, + }, + ], + } + setting.save! + + end +end diff --git a/db/migrate/20161117000003_store_config_name_update_issue_428.rb b/db/migrate/20161117000003_store_config_name_update_issue_428.rb new file mode 100644 index 000000000..a8146c853 --- /dev/null +++ b/db/migrate/20161117000003_store_config_name_update_issue_428.rb @@ -0,0 +1,30 @@ +class StoreConfigNameUpdateIssue428 < ActiveRecord::Migration + def up + # return if it's a new setup + return if !Setting.find_by(name: 'system_init_done') + setting = Setting.find_by(name: 'storage') + return if !Setting + setting.name = 'storage_provider' + setting.options = { + form: [ + { + display: '', + null: true, + name: 'storage_provider', + tag: 'select', + tranlate: true, + options: { + 'DB' => 'Database', + 'File' => 'Filesystem', + }, + }, + ], + } + setting.preferences = { + controller: 'SettingsAreaStorageProvider', + online_service_disable: true, + permission: ['admin.system'], + } + setting.save! + end +end diff --git a/db/seeds.rb b/db/seeds.rb index 52b61445c..b584368de 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -259,7 +259,7 @@ Setting.create_if_not_exists( Setting.create_if_not_exists( title: 'Storage Mechanism', - name: 'storage', + name: 'storage_provider', area: 'System::Storage', description: '"Database" stores all attachments in the database (not recommended for storing large amounts of data). "Filesystem" stores the data on the filesystem. You can switch between the modules even on a system that is already in production without any loss of data.', options: { @@ -267,17 +267,19 @@ Setting.create_if_not_exists( { display: '', null: true, - name: 'storage', + name: 'storage_provider', tag: 'select', + tranlate: true, options: { 'DB' => 'Database', - 'FS' => 'Filesystem', + 'File' => 'Filesystem', }, }, ], }, state: 'DB', preferences: { + controller: 'SettingsAreaStorageProvider', online_service_disable: true, permission: ['admin.system'], }, @@ -1137,10 +1139,10 @@ Setting.create_if_not_exists( title: 'Ticket Hook Position', name: 'ticket_hook_position', area: 'Ticket::Base', - description: "@T('The format of the subject.') -* @T('**Right** means **Some Subject [Ticket#12345]**') -* @T('**Left** means **[Ticket#12345] Some Subject**') -* @T('**None** means **Some Subject** (without ticket number). In the last case you should enable *postmaster_follow_up_search_in* to recognize followups based on email headers and/or body.')", + description: "The format of the subject. +* **Right** means **Some Subject [Ticket#12345]** +* **Left** means **[Ticket#12345] Some Subject** +* **None** means **Some Subject** (without ticket number). In the last case you should enable *postmaster_follow_up_search_in* to recognize followups based on email headers and/or body.", options: { form: [ { @@ -1148,6 +1150,7 @@ Setting.create_if_not_exists( null: true, name: 'ticket_hook_position', tag: 'select', + translate: true, options: { 'left' => 'left', 'right' => 'right', @@ -1158,6 +1161,7 @@ Setting.create_if_not_exists( }, state: 'right', preferences: { + controller: 'SettingsAreaTicketHookPosition', permission: ['admin.ticket'], }, frontend: false @@ -1166,11 +1170,9 @@ Setting.create_if_not_exists( title: 'Ticket Number Format', name: 'ticket_number', area: 'Ticket::Number', - description: "@T('Selects the ticket number generator module.') -* @T('**Increment** increments the ticket number, the SystemID and the counter are used with SystemID.Counter format (e.g. 1010138, 1010139).') -* @T('With **Date** the ticket numbers will be generated by the current date, the SystemID and the counter. The format looks like Year.Month.Day.SystemID.counter (e.g. 201206231010138, 201206231010139).') - -@T('With param 'Checksum => true' the counter will be appended as checksum to the string. The format looks like SystemID.Counter.CheckSum (e. g. 10101384, 10101392) or Year.Month.Day.SystemID.Counter.CheckSum (e.g. 2012070110101520, 2012070110101535).')", + description: "Selects the ticket number generator module. +* **Increment** increments the ticket number, the SystemID and the counter are used with SystemID.Counter format (e.g. 1010138, 1010139). +* With **Date** the ticket numbers will be generated by the current date, the SystemID and the counter. The format looks like Year.Month.Day.SystemID.counter (e.g. 201206231010138, 201206231010139).", options: { form: [ { @@ -1178,6 +1180,7 @@ Setting.create_if_not_exists( null: true, name: 'ticket_number', tag: 'select', + translate: true, options: { 'Ticket::Number::Increment' => 'Increment (SystemID.Counter)', 'Ticket::Number::Date' => 'Date (Year.Month.Day.SystemID.Counter)', @@ -1187,6 +1190,8 @@ Setting.create_if_not_exists( }, state: 'Ticket::Number::Increment', preferences: { + settings_included: %w(ticket_number_increment ticket_number_date), + controller: 'SettingsAreaTicketNumber', permission: ['admin.ticket'], }, frontend: false @@ -1244,6 +1249,7 @@ Setting.create_if_not_exists( }, preferences: { permission: ['admin.ticket'], + hidden: true, }, frontend: false ) @@ -1267,10 +1273,11 @@ Setting.create_if_not_exists( ], }, state: { - checksum: false, + checksum: false }, preferences: { permission: ['admin.ticket'], + hidden: true, }, frontend: false ) diff --git a/lib/import/otrs.rb b/lib/import/otrs.rb index 0bc0fd7f2..eb5a6971e 100644 --- a/lib/import/otrs.rb +++ b/lib/import/otrs.rb @@ -632,13 +632,20 @@ module Import::OTRS ticket_new.delete(:owner) end + record['Articles'].each { |article| + # utf8 encode + _utf8_encode(article) + # lookup customers to create first + _article_based_customers(article) + } + # find customer if ticket_new[:customer] user = User.lookup(login: ticket_new[:customer].downcase) ticket_new[:customer_id] = if user user.id else - 1 + _first_customer_id(record['Articles']) end ticket_new.delete(:customer) else @@ -664,16 +671,6 @@ module Import::OTRS end end - # utf8 encode - record['Articles'].each { |article| - _utf8_encode(article) - } - - # lookup customers to create first - record['Articles'].each { |article| - _article_based_customers(article) - } - record['Articles'].each do |article| retries = 3 @@ -1598,4 +1595,16 @@ module Import::OTRS %w(ProcessManagementProcessID ProcessManagementActivityID ZammadMigratorChanged ZammadMigratorChangedOld) end + def self._first_customer_id(articles) + user_id = 1 + articles.each { |article| + next if article['sender'] != 'customer' + next if article['from'].empty? + + user_id = article['created_by_id'].to_i + break + } + + user_id + end end diff --git a/lib/search_index_backend.rb b/lib/search_index_backend.rb index 321d81f38..819db77ce 100644 --- a/lib/search_index_backend.rb +++ b/lib/search_index_backend.rb @@ -339,8 +339,6 @@ get count of tickets and tickets which match on selector end def self.selector2query(selector, _current_user, aggs_interval, limit) - filter_must = [] - filter_must_not = [] query_must = [] query_must_not = [] if selector && !selector.empty? @@ -355,9 +353,9 @@ get count of tickets and tickets which match on selector t[:term][key_tmp] = data['value'] end if data['operator'] == 'is' - filter_must.push t + query_must.push t elsif data['operator'] == 'is not' - filter_must_not.push t + query_must_not.push t elsif data['operator'] == 'contains' query_must.push t elsif data['operator'] == 'contains not' @@ -391,43 +389,18 @@ get count of tickets and tickets which match on selector from: aggs_interval[:from], to: aggs_interval[:to], } - filter_must.push r + query_must.push r end - if !query_must.empty? || !query_must_not.empty? - if !data[:query][:filtered] - data[:query][:filtered] = {} - end - if !data[:query][:filtered][:query] - data[:query][:filtered][:query] = {} - end - if !data[:query][:filtered][:query][:bool] - data[:query][:filtered][:query][:bool] = {} - end + if !data[:query][:bool] + data[:query][:bool] = {} end + if !query_must.empty? - data[:query][:filtered][:query][:bool][:must] = query_must + data[:query][:bool][:must] = query_must end if !query_must_not.empty? - data[:query][:filtered][:query][:bool][:must_not] = query_must_not - end - - if !filter_must.empty? || !filter_must.empty? - if !data[:query][:filtered] - data[:query][:filtered] = {} - end - if !data[:query][:filtered][:filter] - data[:query][:filtered][:filter] = {} - end - if !data[:query][:filtered][:filter][:bool] - data[:query][:filtered][:filter][:bool] = {} - end - end - if !filter_must.empty? - data[:query][:filtered][:filter][:bool][:must] = filter_must - end - if !filter_must_not.empty? - data[:query][:filtered][:filter][:bool][:must_not] = filter_must_not + data[:query][:bool][:must_not] = query_must_not end # add sort diff --git a/public/assets/tests/form.js b/public/assets/tests/form.js index 2343a2ccc..e2971e153 100644 --- a/public/assets/tests/form.js +++ b/public/assets/tests/form.js @@ -715,13 +715,14 @@ test("form postmaster filter", function() { }, }, set: { - 'x-zammad-ticket-customer': { - value: 'customer' + 'x-zammad-ticket-customer_id': { + value: 'customer', + value_completion: '' }, 'x-zammad-ticket-group_id': { value: '1' }, - 'x-zammad-ticket-owner': { + 'x-zammad-ticket-owner_id': { value: 'owner', value_completion: '' }, @@ -757,13 +758,14 @@ test("form postmaster filter", function() { } }, set: { - 'x-zammad-ticket-customer': { - value: 'customer' + 'x-zammad-ticket-customer_id': { + value: 'customer', + value_completion: '' }, 'x-zammad-ticket-group_id': { value: '1' }, - 'x-zammad-ticket-owner': { + 'x-zammad-ticket-owner_id': { value: 'owner', value_completion: '' }, @@ -774,7 +776,7 @@ test("form postmaster filter", function() { }; deepEqual(params, test_params, 'form param check') el.find('[name="set::x-zammad-ticket-priority_id::value"]').closest('.js-filterElement').find('.js-remove').click() - el.find('[name="set::x-zammad-ticket-customer::value"]').closest('.js-filterElement').find('.js-remove').click() + el.find('[name="set::x-zammad-ticket-customer_id::value"]').closest('.js-filterElement').find('.js-remove').click() App.Delay.set(function() { test("form param check after remove click", function() { params = App.ControllerForm.params(el) @@ -792,7 +794,7 @@ test("form postmaster filter", function() { } }, set: { - 'x-zammad-ticket-owner': { + 'x-zammad-ticket-owner_id': { value: 'owner', value_completion: '' }, diff --git a/test/unit/ticket_overview_test.rb b/test/unit/ticket_overview_test.rb index a31a5076d..44476dd4b 100644 --- a/test/unit/ticket_overview_test.rb +++ b/test/unit/ticket_overview_test.rb @@ -8,6 +8,14 @@ class TicketOverviewTest < ActiveSupport::TestCase customer1 = nil customer2 = nil customer3 = nil + overview1 = nil + overview2 = nil + overview3 = nil + overview4 = nil + overview5 = nil + overview6 = nil + overview7 = nil + overview8 = nil test 'aaa - setup' do # create base @@ -93,7 +101,7 @@ class TicketOverviewTest < ActiveSupport::TestCase Overview.destroy_all UserInfo.current_user_id = 1 overview_role = Role.find_by(name: 'Agent') - Overview.create_or_update( + overview1 = Overview.create_or_update( name: 'My assigned Tickets', link: 'my_assigned', prio: 1000, @@ -120,7 +128,7 @@ class TicketOverviewTest < ActiveSupport::TestCase }, ) - Overview.create_or_update( + overview2 = Overview.create_or_update( name: 'Unassigned & Open', link: 'all_unassigned', prio: 1010, @@ -146,7 +154,7 @@ class TicketOverviewTest < ActiveSupport::TestCase view_mode_default: 's', }, ) - Overview.create_or_update( + overview3 = Overview.create_or_update( name: 'My Tickets 2', link: 'my_tickets_2', prio: 1020, @@ -173,9 +181,36 @@ class TicketOverviewTest < ActiveSupport::TestCase view_mode_default: 's', }, ) + overview4 = Overview.create_or_update( + name: 'My Tickets only with Note', + link: 'my_tickets_onyl_with_note', + prio: 1030, + role_id: overview_role.id, + user_ids: [agent1.id], + condition: { + 'article.type_id' => { + operator: 'is', + value: Ticket::Article::Type.find_by(name: 'note').id, + }, + 'ticket.owner_id' => { + operator: 'is', + pre_condition: 'current_user.id', + }, + }, + order: { + by: 'created_at', + direction: 'ASC', + }, + view: { + d: %w(title customer group created_at), + s: %w(title customer group created_at), + m: %w(number title customer group created_at), + view_mode_default: 's', + }, + ) overview_role = Role.find_by(name: 'Customer') - Overview.create_or_update( + overview5 = Overview.create_or_update( name: 'My Tickets', link: 'my_tickets', prio: 1100, @@ -201,7 +236,7 @@ class TicketOverviewTest < ActiveSupport::TestCase view_mode_default: 's', }, ) - Overview.create_or_update( + overview6 = Overview.create_or_update( name: 'My Organization Tickets', link: 'my_organization_tickets', prio: 1200, @@ -228,7 +263,7 @@ class TicketOverviewTest < ActiveSupport::TestCase view_mode_default: 's', }, ) - Overview.create_or_update( + overview7 = Overview.create_or_update( name: 'My Organization Tickets (open)', link: 'my_organization_tickets_open', prio: 1200, @@ -258,7 +293,7 @@ class TicketOverviewTest < ActiveSupport::TestCase ) overview_role = Role.find_by(name: 'Admin') - Overview.create_or_update( + overview8 = Overview.create_or_update( name: 'Not Shown Admin', link: 'not_shown_admin', prio: 9900, @@ -282,14 +317,15 @@ class TicketOverviewTest < ActiveSupport::TestCase ) end - test 'ticket create' do + test 'bbb overiview index' do result = Ticket::Overviews.all( current_user: agent1, ) - assert_equal(2, result.count) + assert_equal(3, result.count) assert_equal('My assigned Tickets', result[0].name) assert_equal('Unassigned & Open', result[1].name) + assert_equal('My Tickets only with Note', result[2].name) result = Ticket::Overviews.all( current_user: agent2, @@ -322,4 +358,501 @@ class TicketOverviewTest < ActiveSupport::TestCase end + test 'ccc overiview content' do + + Ticket.destroy_all + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + assert_equal(result[2][:count], 0) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + ticket1 = Ticket.create( + title: 'overview test 1', + group: Group.lookup(name: 'OverviewTest'), + customer_id: 2, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + updated_by_id: 1, + created_by_id: 1, + ) + article1 = Ticket::Article.create( + ticket_id: ticket1.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + body: 'some message... 123', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Customer'), + type: Ticket::Article::Type.find_by(name: 'email'), + updated_by_id: 1, + created_by_id: 1, + ) + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket1.id) + assert_equal(result[1][:count], 1) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + assert_equal(result[2][:count], 0) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + ticket2 = Ticket.create( + title: 'overview test 2', + group: Group.lookup(name: 'OverviewTest'), + customer_id: 2, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '3 high'), + updated_by_id: 1, + created_by_id: 1, + ) + article2 = Ticket::Article.create( + ticket_id: ticket2.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + body: 'some message... 123', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Agent'), + type: Ticket::Article::Type.find_by(name: 'note'), + updated_by_id: 1, + created_by_id: 1, + ) + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket1.id) + assert_equal(result[1][:tickets][1][:id], ticket2.id) + assert_equal(result[1][:count], 2) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + assert_equal(result[2][:count], 0) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + ticket2.owner_id = agent1.id + ticket2.save! + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[0][:tickets][0][:id], ticket2.id) + assert_equal(result[0][:count], 1) + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket1.id) + assert_equal(result[1][:count], 1) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert_equal(result[2][:tickets][0][:id], ticket2.id) + assert_equal(result[2][:count], 1) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + ticket3 = Ticket.create( + title: 'overview test 3', + group: Group.lookup(name: 'OverviewTest'), + customer_id: 2, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '1 low'), + updated_by_id: 1, + created_by_id: 1, + ) + article3 = Ticket::Article.create( + ticket_id: ticket3.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + body: 'some message... 123', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Customer'), + type: Ticket::Article::Type.find_by(name: 'email'), + updated_by_id: 1, + created_by_id: 1, + ) + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[0][:tickets][0][:id], ticket2.id) + assert_equal(result[0][:count], 1) + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket1.id) + assert_equal(result[1][:tickets][1][:id], ticket3.id) + assert_equal(result[1][:count], 2) + assert_equal(result[2][:overview][:id], overview4.id) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert_equal(result[2][:tickets][0][:id], ticket2.id) + assert_equal(result[2][:count], 1) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:id], overview3.id) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + overview2.order = { + by: 'created_at', + direction: 'DESC', + } + overview2.save! + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[0][:tickets][0][:id], ticket2.id) + assert_equal(result[0][:count], 1) + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket3.id) + assert_equal(result[1][:tickets][1][:id], ticket1.id) + assert_equal(result[1][:count], 2) + assert_equal(result[2][:overview][:id], overview4.id) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert_equal(result[2][:tickets][0][:id], ticket2.id) + assert_equal(result[2][:count], 1) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:id], overview3.id) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + overview2.order = { + by: 'priority_id', + direction: 'DESC', + } + overview2.save! + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[0][:tickets][0][:id], ticket2.id) + assert_equal(result[0][:count], 1) + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket1.id) + assert_equal(result[1][:tickets][1][:id], ticket3.id) + assert_equal(result[1][:count], 2) + assert_equal(result[2][:overview][:id], overview4.id) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert_equal(result[2][:tickets][0][:id], ticket2.id) + assert_equal(result[2][:count], 1) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:id], overview3.id) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + overview2.order = { + by: 'priority_id', + direction: 'ASC', + } + overview2.save! + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[0][:tickets][0][:id], ticket2.id) + assert_equal(result[0][:count], 1) + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket3.id) + assert_equal(result[1][:tickets][1][:id], ticket1.id) + assert_equal(result[1][:count], 2) + assert_equal(result[2][:overview][:id], overview4.id) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert_equal(result[2][:tickets][0][:id], ticket2.id) + assert_equal(result[2][:count], 1) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:id], overview3.id) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + overview2.order = { + by: 'priority', + direction: 'DESC', + } + overview2.save! + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[0][:tickets][0][:id], ticket2.id) + assert_equal(result[0][:count], 1) + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket1.id) + assert_equal(result[1][:tickets][1][:id], ticket3.id) + assert_equal(result[1][:count], 2) + assert_equal(result[2][:overview][:id], overview4.id) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert_equal(result[2][:tickets][0][:id], ticket2.id) + assert_equal(result[2][:count], 1) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:id], overview3.id) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + overview2.order = { + by: 'priority', + direction: 'ASC', + } + overview2.save! + + result = Ticket::Overviews.index(agent1) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[0][:tickets][0][:id], ticket2.id) + assert_equal(result[0][:count], 1) + assert_equal(result[0][:tickets].class, Array) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert_not(result[1][:tickets].empty?) + assert_equal(result[1][:tickets][0][:id], ticket3.id) + assert_equal(result[1][:tickets][1][:id], ticket1.id) + assert_equal(result[1][:count], 2) + assert_equal(result[2][:overview][:id], overview4.id) + assert_equal(result[2][:overview][:name], 'My Tickets only with Note') + assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note') + assert_equal(result[2][:tickets].class, Array) + assert_equal(result[2][:tickets][0][:id], ticket2.id) + assert_equal(result[2][:count], 1) + + result = Ticket::Overviews.index(agent2) + assert_equal(result[0][:overview][:id], overview1.id) + assert_equal(result[0][:overview][:name], 'My assigned Tickets') + assert_equal(result[0][:overview][:view], 'my_assigned') + assert_equal(result[0][:count], 0) + assert_equal(result[0][:tickets].class, Array) + assert(result[0][:tickets].empty?) + assert_equal(result[1][:overview][:id], overview2.id) + assert_equal(result[1][:overview][:name], 'Unassigned & Open') + assert_equal(result[1][:overview][:view], 'all_unassigned') + assert_equal(result[1][:tickets].class, Array) + assert(result[1][:tickets].empty?) + assert_equal(result[1][:count], 0) + assert_equal(result[2][:overview][:id], overview3.id) + assert_equal(result[2][:overview][:name], 'My Tickets 2') + assert_equal(result[2][:overview][:view], 'my_tickets_2') + assert_equal(result[2][:tickets].class, Array) + assert(result[2][:tickets].empty?) + + end + end diff --git a/test/unit/ticket_trigger_test.rb b/test/unit/ticket_trigger_test.rb index 5d86c2659..91ac530d3 100644 --- a/test/unit/ticket_trigger_test.rb +++ b/test/unit/ticket_trigger_test.rb @@ -4,8 +4,37 @@ require 'test_helper' class TicketTriggerTest < ActiveSupport::TestCase test '1 basic' do trigger1 = Trigger.create_or_update( + name: 'aaa loop check', + condition: { + 'article.subject' => { + 'operator' => 'contains', + 'value' => 'Thanks for your inquiry', + }, + }, + perform: { + 'ticket.tags' => { + 'operator' => 'add', + 'value' => 'should_not_loop', + }, + 'notification.email' => { + 'body' => 'some lala', + 'recipient' => 'ticket_customer', + 'subject' => 'Thanks for your inquiry - loop check (#{ticket.title})!', + }, + }, + disable_notification: true, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + trigger2 = Trigger.create_or_update( name: 'auto reply', condition: { + 'ticket.action' => { + 'operator' => 'is', + 'value' => 'create', + }, 'ticket.state_id' => { 'operator' => 'is', 'value' => Ticket::State.lookup(name: 'new').id.to_s, @@ -31,7 +60,54 @@ class TicketTriggerTest < ActiveSupport::TestCase updated_by_id: 1, ) - trigger2 = Trigger.create_or_update( + trigger3 = Trigger.create_or_update( + name: 'auto tag 1', + condition: { + 'ticket.action' => { + 'operator' => 'is', + 'value' => 'update', + }, + 'ticket.state_id' => { + 'operator' => 'is', + 'value' => Ticket::State.lookup(name: 'new').id.to_s, + } + }, + perform: { + 'ticket.priority_id' => { + 'value' => Ticket::Priority.lookup(name: '3 high').id.to_s, + }, + 'ticket.tags' => { + 'operator' => 'remove', + 'value' => 'kk', + }, + }, + disable_notification: true, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + trigger4 = Trigger.create_or_update( + name: 'auto tag 2', + condition: { + 'ticket.state_id' => { + 'operator' => 'is', + 'value' => Ticket::State.lookup(name: 'new').id.to_s, + } + }, + perform: { + 'ticket.tags' => { + 'operator' => 'add', + 'value' => 'abc', + }, + }, + disable_notification: true, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + trigger5 = Trigger.create_or_update( name: 'not matching', condition: { 'ticket.state_id' => { @@ -50,6 +126,31 @@ class TicketTriggerTest < ActiveSupport::TestCase updated_by_id: 1, ) + trigger6 = Trigger.create_or_update( + name: 'zzz last', + condition: { + 'article.subject' => { + 'operator' => 'contains', + 'value' => 'some subject 1234', + }, + }, + perform: { + 'ticket.tags' => { + 'operator' => 'add', + 'value' => 'article_create_trigger', + }, + 'notification.email' => { + 'body' => 'some lala', + 'recipient' => 'ticket_customer', + 'subject' => 'Thanks for your inquiry - 1234 check (#{ticket.title})!', + }, + }, + disable_notification: true, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + ticket1 = Ticket.create( title: "some title\n äöüß", group: Group.lookup(name: 'Users'), @@ -89,7 +190,7 @@ class TicketTriggerTest < ActiveSupport::TestCase assert_equal('new', ticket1.state.name, 'ticket1.state verify') assert_equal('3 high', ticket1.priority.name, 'ticket1.priority verify') assert_equal(2, ticket1.articles.count, 'ticket1.articles verify') - assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id)) + assert_equal(%w(aa kk abc), Tag.tag_list(object: 'Ticket', o_id: ticket1.id)) article1 = ticket1.articles.last assert_match('Zammad ', article1.from) assert_match('nicole.braun@zammad.org', article1.to) @@ -108,7 +209,7 @@ class TicketTriggerTest < ActiveSupport::TestCase assert_equal('new', ticket1.state.name, 'ticket1.state verify') assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify') assert_equal(2, ticket1.articles.count, 'ticket1.articles verify') - assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id)) + assert_equal(%w(aa kk abc), Tag.tag_list(object: 'Ticket', o_id: ticket1.id)) ticket1.state = Ticket::State.lookup(name: 'open') ticket1.save @@ -120,7 +221,7 @@ class TicketTriggerTest < ActiveSupport::TestCase assert_equal('open', ticket1.state.name, 'ticket1.state verify') assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify') assert_equal(2, ticket1.articles.count, 'ticket1.articles verify') - assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id)) + assert_equal(%w(aa kk abc), Tag.tag_list(object: 'Ticket', o_id: ticket1.id)) ticket1.state = Ticket::State.lookup(name: 'new') ticket1.save @@ -132,14 +233,8 @@ class TicketTriggerTest < ActiveSupport::TestCase assert_equal('Users', ticket1.group.name, 'ticket1.group verify') assert_equal('new', ticket1.state.name, 'ticket1.state verify') assert_equal('3 high', ticket1.priority.name, 'ticket1.priority verify') - assert_equal(3, ticket1.articles.count, 'ticket1.articles verify') - assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id)) - article1 = ticket1.articles.last - assert_match('Zammad ', article1.from) - assert_match('nicole.braun@zammad.org', article1.to) - assert_match('Thanks for your inquiry (some title äöüß)!', article1.subject) - assert_match('Braun
some <b>title</b>', article1.body) - assert_equal('text/html', article1.content_type) + assert_equal(2, ticket1.articles.count, 'ticket1.articles verify') + assert_equal(%w(aa abc), Tag.tag_list(object: 'Ticket', o_id: ticket1.id)) ticket2 = Ticket.create( title: "some title\n äöüß", @@ -179,11 +274,12 @@ class TicketTriggerTest < ActiveSupport::TestCase created_by_id: 1, ) assert(ticket3, 'ticket3 created') + Ticket::Article.create( ticket_id: ticket3.id, from: 'some_sender@example.com', to: 'some_recipient@example.com', - subject: 'some subject', + subject: 'some subject 1234', message_id: 'some@id', content_type: 'text/html', body: 'some message note
new line', @@ -208,9 +304,9 @@ class TicketTriggerTest < ActiveSupport::TestCase assert_equal('Users', ticket3.group.name, 'ticket3.group verify') assert_equal('new', ticket3.state.name, 'ticket3.state verify') assert_equal('3 high', ticket3.priority.name, 'ticket3.priority verify') - assert_equal(2, ticket3.articles.count, 'ticket3.articles verify') - assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket3.id)) - article3 = ticket3.articles.last + assert_equal(3, ticket3.articles.count, 'ticket3.articles verify') + assert_equal(%w(aa kk abc article_create_trigger), Tag.tag_list(object: 'Ticket', o_id: ticket3.id)) + article3 = ticket3.articles[1] assert_match('Zammad ', article3.from) assert_match('nicole.braun@zammad.org', article3.to) assert_match('Thanks for your inquiry (some title äöüß3)!', article3.subject) @@ -218,6 +314,61 @@ class TicketTriggerTest < ActiveSupport::TestCase assert_match('> some message note
> new line', article3.body) assert_no_match('> some message <b>note</b>
> new line', article3.body) assert_equal('text/html', article3.content_type) + article3 = ticket3.articles[2] + assert_match('Zammad ', article3.from) + assert_match('nicole.braun@zammad.org', article3.to) + assert_match('Thanks for your inquiry - 1234 check (some title äöüß3)!', article3.subject) + assert_equal('text/html', article3.content_type) + + Ticket::Article.create( + ticket_id: ticket3.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject - not 1234', + message_id: 'some@id', + content_type: 'text/html', + body: 'some message note
new line', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Agent'), + type: Ticket::Article::Type.find_by(name: 'note'), + updated_by_id: 1, + created_by_id: 1, + ) + + Observer::Transaction.commit + + ticket3 = Ticket.lookup(id: ticket3.id) + assert_equal('some title äöüß3', ticket3.title, 'ticket3.title verify') + assert_equal('Users', ticket3.group.name, 'ticket3.group verify') + assert_equal('new', ticket3.state.name, 'ticket3.state verify') + assert_equal('3 high', ticket3.priority.name, 'ticket3.priority verify') + assert_equal(4, ticket3.articles.count, 'ticket3.articles verify') + assert_equal(%w(aa kk abc article_create_trigger), Tag.tag_list(object: 'Ticket', o_id: ticket3.id)) + + Ticket::Article.create( + ticket_id: ticket3.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject 1234', + message_id: 'some@id', + content_type: 'text/html', + body: 'some message note
new line', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Agent'), + type: Ticket::Article::Type.find_by(name: 'note'), + updated_by_id: 1, + created_by_id: 1, + ) + + Observer::Transaction.commit + + ticket3 = Ticket.lookup(id: ticket3.id) + assert_equal('some title äöüß3', ticket3.title, 'ticket3.title verify') + assert_equal('Users', ticket3.group.name, 'ticket3.group verify') + assert_equal('new', ticket3.state.name, 'ticket3.state verify') + assert_equal('3 high', ticket3.priority.name, 'ticket3.priority verify') + assert_equal(5, ticket3.articles.count, 'ticket3.articles verify') + assert_equal(%w(aa kk abc article_create_trigger), Tag.tag_list(object: 'Ticket', o_id: ticket3.id)) Trigger.destroy_all end