diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 439e74a8e..4612cbc08 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -40,12 +40,12 @@ job_unit_mysql: - mysql script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - rake db:seed - rake test:units - rake test:controllers + - rake db:drop job_unit_postgresql: stage: test @@ -54,12 +54,12 @@ job_unit_postgresql: - postgresql script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - rake db:seed - rake test:units - rake test:controllers + - rake db:drop job_integration_email_helper: stage: test @@ -67,10 +67,10 @@ job_integration_email_helper: - core script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/email_helper_test.rb + - rake db:drop job_integration_twitter: stage: test @@ -79,11 +79,12 @@ job_integration_twitter: - twitter script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - rake db:seed - ruby -I test/ test/integration/twitter_test.rb + - rake db:drop + allow_failure: true job_integration_facebook: stage: test @@ -91,11 +92,12 @@ job_integration_facebook: - core script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - rake db:seed - ruby -I test/ test/integration/facebook_test.rb + - rake db:drop + allow_failure: true job_integration_geo_ip: stage: test @@ -103,10 +105,10 @@ job_integration_geo_ip: - core script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/geo_ip_test.rb + - rake db:drop job_integration_geo_location: stage: test @@ -114,10 +116,10 @@ job_integration_geo_location: - core script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/geo_location_test.rb + - rake db:drop job_integration_geo_calendar: stage: test @@ -125,10 +127,10 @@ job_integration_geo_calendar: - core script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/geo_calendar_test.rb + - rake db:drop job_integration_user_agent: stage: test @@ -136,10 +138,10 @@ job_integration_user_agent: - core script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/user_agent_test.rb + - rake db:drop job_integration_es_mysql: stage: test @@ -150,12 +152,12 @@ job_integration_es_mysql: - export RAILS_ENV=test - export ES_INDEX_RAND=true - export ES_URL="http://localhost:9200" - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/elasticsearch_test.rb - ruby -I test/ test/controllers/search_controller_test.rb - ruby -I test/ test/integration/report_test.rb + - rake db:drop job_integration_es_postgresql: stage: test @@ -166,12 +168,12 @@ job_integration_es_postgresql: - export RAILS_ENV=test - export ES_INDEX_RAND=true - export ES_URL="http://localhost:9200" - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/elasticsearch_test.rb - ruby -I test/ test/controllers/search_controller_test.rb - ruby -I test/ test/integration/report_test.rb + - rake db:drop job_integration_zendesk_mysql: stage: test @@ -180,10 +182,10 @@ job_integration_zendesk_mysql: - mysql script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/zendesk_import_test.rb + - rake db:drop job_integration_zendesk_postgresql: stage: test @@ -192,10 +194,10 @@ job_integration_zendesk_postgresql: - postgresql script: - export RAILS_ENV=test - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/zendesk_import_test.rb + - rake db:drop job_integration_otrs_5_mysql: stage: test @@ -205,10 +207,10 @@ job_integration_otrs_5_mysql: script: - export RAILS_ENV=test - export IMPORT_OTRS_ENDPOINT="http://vz599.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/otrs_import_test.rb + - rake db:drop job_integration_otrs_5_postgresql: stage: test @@ -218,10 +220,10 @@ job_integration_otrs_5_postgresql: script: - export RAILS_ENV=test - export IMPORT_OTRS_ENDPOINT="http://vz599.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/otrs_import_test.rb + - rake db:drop job_integration_otrs_4: stage: test @@ -230,10 +232,10 @@ job_integration_otrs_4: script: - export RAILS_ENV=test - export IMPORT_OTRS_ENDPOINT="http://vz383.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/otrs_import_test.rb + - rake db:drop job_integration_otrs_33: stage: test @@ -242,10 +244,10 @@ job_integration_otrs_33: script: - export RAILS_ENV=test - export IMPORT_OTRS_ENDPOINT="http://vz305.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/otrs_import_test.rb + - rake db:drop job_integration_otrs_32: stage: test @@ -254,10 +256,10 @@ job_integration_otrs_32: script: - export RAILS_ENV=test - export IMPORT_OTRS_ENDPOINT="http://vz382.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/otrs_import_test.rb + - rake db:drop job_integration_otrs_31: stage: test @@ -266,10 +268,10 @@ job_integration_otrs_31: script: - export RAILS_ENV=test - export IMPORT_OTRS_ENDPOINT="http://vz381.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - - rake db:drop; - rake db:create - rake db:migrate - ruby -I test/ test/integration/otrs_import_test.rb + - rake db:drop job_integration_twitter_ff: stage: browser diff --git a/Gemfile b/Gemfile index bcdb47fe8..ecc9a8afd 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' ruby '2.2.3' -gem 'rails', '4.2.5.2' +gem 'rails', '4.2.6' gem 'rails-observers' gem 'activerecord-session_store' @@ -122,7 +122,7 @@ group :development, :test do end -gem 'puma', '< 3.0' +gem 'puma' # load onw gem's local_gemfile = File.join(File.dirname(__FILE__), 'Gemfile.local') diff --git a/Gemfile.lock b/Gemfile.lock index c5cd62765..d51b0aba6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,40 +1,40 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (4.2.5.2) - actionpack (= 4.2.5.2) - actionview (= 4.2.5.2) - activejob (= 4.2.5.2) + actionmailer (4.2.6) + actionpack (= 4.2.6) + actionview (= 4.2.6) + activejob (= 4.2.6) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.5.2) - actionview (= 4.2.5.2) - activesupport (= 4.2.5.2) + actionpack (4.2.6) + actionview (= 4.2.6) + activesupport (= 4.2.6) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.5.2) - activesupport (= 4.2.5.2) + actionview (4.2.6) + activesupport (= 4.2.6) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (4.2.5.2) - activesupport (= 4.2.5.2) + activejob (4.2.6) + activesupport (= 4.2.6) globalid (>= 0.3.0) - activemodel (4.2.5.2) - activesupport (= 4.2.5.2) + activemodel (4.2.6) + activesupport (= 4.2.6) builder (~> 3.1) - activerecord (4.2.5.2) - activemodel (= 4.2.5.2) - activesupport (= 4.2.5.2) + activerecord (4.2.6) + activemodel (= 4.2.6) + activesupport (= 4.2.6) arel (~> 6.0) activerecord-session_store (0.1.2) actionpack (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5) railties (>= 4.0.0, < 5) - activesupport (4.2.5.2) + activesupport (4.2.6) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -45,7 +45,7 @@ GEM ast (2.2.0) autoprefixer-rails (6.3.3.1) execjs - biz (1.3.4) + biz (1.4.0) clavius (~> 1.0) tzinfo browser (2.0.2) @@ -76,7 +76,7 @@ GEM diffy (3.1.0) dnsruby (1.59.2) docile (1.1.5) - domain_name (0.5.20160216) + domain_name (0.5.20160309) unf (>= 0.0.5, < 1.0.0) eco (1.0.0) coffee-script @@ -90,7 +90,7 @@ GEM dnsruby (>= 1.5) equalizer (0.0.10) erubis (2.7.0) - eventmachine (1.0.9.1) + eventmachine (1.2.0.1) execjs (2.6.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) @@ -172,8 +172,8 @@ GEM rack (>= 1.0, < 3) omniauth-facebook (3.0.0) omniauth-oauth2 (~> 1.2) - omniauth-google-oauth2 (0.3.1) - jwt (~> 1.0) + omniauth-google-oauth2 (0.4.0) + jwt (~> 1.5.0) multi_json (~> 1.3) omniauth (>= 1.1.1) omniauth-oauth2 (>= 1.3.1) @@ -199,22 +199,22 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - puma (2.16.0) + puma (3.1.0) rack (1.6.4) rack-livereload (0.3.16) rack rack-test (0.6.3) rack (>= 1.0) - rails (4.2.5.2) - actionmailer (= 4.2.5.2) - actionpack (= 4.2.5.2) - actionview (= 4.2.5.2) - activejob (= 4.2.5.2) - activemodel (= 4.2.5.2) - activerecord (= 4.2.5.2) - activesupport (= 4.2.5.2) + rails (4.2.6) + actionmailer (= 4.2.6) + actionpack (= 4.2.6) + actionview (= 4.2.6) + activejob (= 4.2.6) + activemodel (= 4.2.6) + activerecord (= 4.2.6) + activesupport (= 4.2.6) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.5.2) + railties (= 4.2.6) sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) @@ -226,23 +226,23 @@ GEM loofah (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.2.5.2) - actionpack (= 4.2.5.2) - activesupport (= 4.2.5.2) + railties (4.2.6) + actionpack (= 4.2.6) + activesupport (= 4.2.6) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.1.0) - rake (10.5.0) + rake (11.1.1) rb-fsevent (0.9.7) rb-inotify (0.9.7) ffi (>= 0.5.0) ref (2.0.0) - rubocop (0.37.2) - parser (>= 2.3.0.4, < 3.0) + rubocop (0.38.0) + parser (>= 2.3.0.6, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) - unicode-display_width (~> 0.3) + unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.7.5) rubyzip (1.2.0) sass (3.4.21) @@ -253,9 +253,8 @@ GEM sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) scrub_rb (1.0.1) - selenium-webdriver (2.52.0) + selenium-webdriver (2.53.0) childprocess (~> 0.5) - multi_json (~> 1.0) rubyzip (~> 1.0) websocket (~> 1.0) shellany (0.0.1) @@ -273,7 +272,7 @@ GEM sprockets (3.5.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.0.3) + sprockets-rails (3.0.4) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) @@ -305,7 +304,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicode-display_width (0.3.1) + unicode-display_width (1.0.2) websocket (1.2.2) writeexcel (1.0.5) zendesk_api (1.13.4) @@ -352,9 +351,9 @@ DEPENDENCIES omniauth-linkedin omniauth-twitter pre-commit - puma (< 3.0) + puma rack-livereload - rails (= 4.2.5.2) + rails (= 4.2.6) rails-observers rb-fsevent rubocop diff --git a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee index 50f586140..86f22598e 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee @@ -728,6 +728,9 @@ class App.WizardModal extends App.Controller if type @$(".#{screen}").find("[name=\"options::#{field}\"]").closest('.form-group').addClass('has-error') + render: -> + # do nothing + class App.WizardFullScreen extends App.WizardModal className: 'getstarted fit' diff --git a/app/assets/javascripts/app/controllers/_ui_element/active.coffee b/app/assets/javascripts/app/controllers/_ui_element/active.coffee index e7947ed62..56d0b363e 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/active.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/active.coffee @@ -17,13 +17,13 @@ class App.UiElement.active extends App.UiElement.ApplicationUiElement attribute.name = '{boolean}' + attribute.name # build options list based on config - @getConfigOptionList( attribute, params ) + @getConfigOptionList(attribute, params) # sort attribute.options - @sortOptions( attribute, params ) + @sortOptions(attribute, params) # finde selected/checked item of list - @selectedOptions( attribute, params ) + @selectedOptions(attribute, params) # return item $( App.view('generic/select')( attribute: attribute ) ) \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/_ui_element/boolean.coffee b/app/assets/javascripts/app/controllers/_ui_element/boolean.coffee index fac677ef0..04951ae39 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/boolean.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/boolean.coffee @@ -14,13 +14,13 @@ class App.UiElement.boolean extends App.UiElement.ApplicationUiElement attribute.name = '{boolean}' + attribute.name # build options list based on config - @getConfigOptionList( attribute, params ) + @getConfigOptionList(attribute, params) # sort attribute.options - @sortOptions( attribute, params ) + @sortOptions(attribute, params) # finde selected/checked item of list - @selectedOptions( attribute, params ) + @selectedOptions(attribute, params) # return item - $( App.view('generic/select')( attribute: attribute ) ) \ No newline at end of file + $(App.view('generic/select')(attribute: attribute)) \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/_ui_element/radio.coffee b/app/assets/javascripts/app/controllers/_ui_element/radio.coffee index 6b03e62f8..00c5ec5b6 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/radio.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/radio.coffee @@ -3,24 +3,24 @@ class App.UiElement.radio extends App.UiElement.ApplicationUiElement @render: (attribute, params) -> # build options list based on config - @getConfigOptionList( attribute, params ) + @getConfigOptionList(attribute, params) # build options list based on relation - @getRelationOptionList( attribute, params ) + @getRelationOptionList(attribute, params) # add null selection if needed - @addNullOption( attribute, params ) + @addNullOption(attribute, params) # sort attribute.options - @sortOptions( attribute, params ) + @sortOptions(attribute, params) # finde selected/checked item of list - @selectedOptions( attribute, params ) + @selectedOptions(attribute, params) # disable item of list - @disabledOptions( attribute, params ) + @disabledOptions(attribute, params) # filter attributes - @filterOption( attribute, params ) + @filterOption(attribute, params) $( App.view('generic/radio')( attribute: attribute ) ) diff --git a/app/assets/javascripts/app/controllers/_ui_element/select.coffee b/app/assets/javascripts/app/controllers/_ui_element/select.coffee index 91a3ad8db..ada6c0b07 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/select.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/select.coffee @@ -9,25 +9,25 @@ class App.UiElement.select extends App.UiElement.ApplicationUiElement attribute.multiple = '' # build options list based on config - @getConfigOptionList( attribute, params ) + @getConfigOptionList(attribute, params) # build options list based on relation - @getRelationOptionList( attribute, params ) + @getRelationOptionList(attribute, params) # add null selection if needed - @addNullOption( attribute, params ) + @addNullOption(attribute, params) # sort attribute.options - @sortOptions( attribute, params ) + @sortOptions(attribute, params) # finde selected/checked item of list - @selectedOptions( attribute, params ) + @selectedOptions(attribute, params) # disable item of list - @disabledOptions( attribute, params ) + @disabledOptions(attribute, params) # filter attributes - @filterOption( attribute, params ) + @filterOption(attribute, params) # return item $( App.view('generic/select')( attribute: attribute ) ) \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/_ui_element/tag.coffee b/app/assets/javascripts/app/controllers/_ui_element/tag.coffee index 3fb865259..701bae89b 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/tag.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/tag.coffee @@ -5,5 +5,5 @@ class App.UiElement.tag a = -> $('#' + attribute.id ).tokenfield() $('#' + attribute.id ).parent().css('height', 'auto') - App.Delay.set( a, 120, undefined, 'tags' ) + App.Delay.set(a, 120, undefined, 'tags') item \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/_ui_element/textarea.coffee b/app/assets/javascripts/app/controllers/_ui_element/textarea.coffee index f0e7fc601..2a63da75f 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/textarea.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/textarea.coffee @@ -13,7 +13,7 @@ class App.UiElement.textarea if visible && !$( item[0] ).expanding('active') $( item[0] ).expanding().focus() ) - App.Delay.set( a, 80 ) + App.Delay.set(a, 80) if attribute.upload @@ -39,5 +39,5 @@ class App.UiElement.textarea fail: '' debug: false ) - App.Delay.set( u, 100, undefined, 'form_upload' ) + App.Delay.set(u, 100, undefined, 'form_upload') item \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee b/app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee index 9d1322a9e..b12765ee8 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee @@ -36,7 +36,7 @@ class App.UiElement.ticket_perform_action item.find('.js-attributeSelector').prepend(selector) # add filter - item.find('.js-add').bind('click', (e) -> + item.find('.js-add').bind('click', (e) => element = $(e.target).closest('.js-filterElement') elementClone = element.clone(true) element.after(elementClone) diff --git a/app/assets/javascripts/app/controllers/_ui_element/timer.coffee b/app/assets/javascripts/app/controllers/_ui_element/timer.coffee new file mode 100644 index 000000000..860ccb8ce --- /dev/null +++ b/app/assets/javascripts/app/controllers/_ui_element/timer.coffee @@ -0,0 +1,138 @@ +# coffeelint: disable=camel_case_classes +class App.UiElement.timer + @render: (attribute) -> + days = + Mon: 'Monday' + Tue: 'Tuesday' + Wed: 'Wednesday' + Thu: 'Thursday' + Fri: 'Friday' + Sat: 'Saturday' + Sun: 'Sunday' + hours = + 0: '12 am' + 1: '1 am' + 2: '2 am' + 3: '3 am' + 4: '4 am' + 5: '5 am' + 6: '6 am' + 7: '7 am' + 8: '8 am' + 9: '9 am' + 10: '10 am' + 11: '11 am' + 12: '12 am' + 13: '1 pm' + 14: '2 pm' + 15: '3 pm' + 16: '4 pm' + 17: '5 pm' + 18: '6 pm' + 19: '7 pm' + 20: '8 pm' + 21: '9 pm' + 22: '10 pm' + 23: '11 pm' + hours = + 0: '00' + 1: '01' + 2: '02' + 3: '03' + 4: '04' + 5: '05' + 6: '06' + 7: '07' + 8: '08' + 9: '09' + 10: '10' + 11: '11' + 12: '12' + 13: '13' + 14: '14' + 15: '15' + 16: '16' + 17: '17' + 18: '18' + 19: '19' + 20: '20' + 21: '21' + 22: '22' + 23: '23' + minutes = + 0: '00' + 10: '10' + 20: '20' + 30: '30' + 40: '40' + 50: '50' + + if !attribute.value + attribute.value = {} + if _.isEmpty(attribute.value.days) + attribute.value.days = + Mon: true + if _.isEmpty(attribute.value.hours) + attribute.value.hours = + 0: true + if _.isEmpty(attribute.value.minutes) + attribute.value.minutes = + 0: true + + timer = $( App.view('generic/timer')( attribute: attribute, days: days, hours: hours, minutes: minutes ) ) + + timer.find('.select-value').bind('click', (e) => + @select(e) + ) + @createOutputString(timer) + + timer + + @select: (e) => + target = $(e.currentTarget) + + if target.hasClass('is-selected') + # prevent zero selections + if target.siblings('.is-selected').size() > 0 + target.removeClass('is-selected') + target.next().val('false') + else + target.addClass('is-selected') + target.next().val('true') + + formGroup = $(e.currentTarget).closest('.form-group') + @createOutputString(formGroup) + + @createOutputString: (formGroup) => + days = $.map(formGroup.find('[data-type=day]').filter('.is-selected'), (el) -> return $(el).text() ) + hours = $.map(formGroup.find('[data-type=hour]').filter('.is-selected'), (el) -> return $(el).text() ) + minutes = $.map(formGroup.find('[data-type=minute]').filter('.is-selected'), (el) -> return $(el).text() ) + + hours = @injectMinutes(hours, minutes) + + days = @joinItems days + hours = @joinItems hours + + formGroup.find('.js-timerResult').text(App.i18n.translateInline('Run every %s at %s', days, hours)) + + @injectMinutes: (hours, minutes) -> + newHours = [] # hours.length x minutes.length long + + for hour in hours + # split off am/pm + [hour, suffix] = hour.split(' ') + + for minute in minutes + combined = "#{ hour }:#{ minute }" + combined += " #{suffix}" if suffix + + newHours.push combined + + newHours + + @joinItems: (items) -> + switch items.length + when 1 then return items[0] + when 2 then return "#{ items[0] } #{App.i18n.translateInline('and')} #{ items[1] }" + else + return "#{ items.slice(0, -1).join(', ') } #{App.i18n.translateInline('and')} #{ items[items.length-1] }" diff --git a/app/assets/javascripts/app/controllers/_ui_element/timezone.coffee b/app/assets/javascripts/app/controllers/_ui_element/timezone.coffee index a72413280..ce78512cd 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/timezone.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/timezone.coffee @@ -15,13 +15,13 @@ class App.UiElement.timezone extends App.UiElement.ApplicationUiElement attribute.options.push item # add null selection if needed - @addNullOption( attribute, params ) + @addNullOption(attribute, params) # sort attribute.options - @sortOptions( attribute, params ) + @sortOptions(attribute, params) # finde selected/checked item of list - @selectedOptions( attribute, params ) + @selectedOptions(attribute, params) attribute.tag = 'searchable_select' attribute.placeholder = App.i18n.translateInline('Enter timzone...') diff --git a/app/assets/javascripts/app/controllers/getting_started.coffee b/app/assets/javascripts/app/controllers/getting_started.coffee index f9541a037..6809e910a 100644 --- a/app/assets/javascripts/app/controllers/getting_started.coffee +++ b/app/assets/javascripts/app/controllers/getting_started.coffee @@ -59,7 +59,7 @@ class Index extends App.WizardFullScreen App.Config.set( 'getting_started', Index, 'Routes' ) -class AutoWizard extends App.ControllerContent +class AutoWizard extends App.WizardFullScreen constructor: -> super diff --git a/app/assets/javascripts/app/controllers/job.coffee b/app/assets/javascripts/app/controllers/job.coffee new file mode 100644 index 000000000..3d29dc0ee --- /dev/null +++ b/app/assets/javascripts/app/controllers/job.coffee @@ -0,0 +1,29 @@ +class Index extends App.ControllerContent + constructor: -> + super + + # check authentication + return if !@authenticate(false, 'Admin') + + new App.ControllerGenericIndex( + el: @el + id: @id + genericObject: 'Job' + defaultSortBy: 'name' + pageData: + title: 'Scheduler' + home: 'Jobs' + object: 'Scheduler' + objects: 'Schedulers' + navupdate: '#Jobs' + notes: [ + 'Scheduler are ...' + ] + buttons: [ + { name: 'New Scheduler', 'data-type': 'new', class: 'btn--success' } + ] + container: @el.closest('.content') + #large: true + ) + +App.Config.set('Job', { prio: 3400, name: 'Scheduler', parent: '#manage', target: '#manage/job', controller: Index, role: ['Admin'] }, 'NavBarAdmin') diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee index 68770b19b..8b3c12112 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_actions.coffee @@ -77,21 +77,38 @@ class App.TicketZoomArticleActions extends App.Controller href: '#' } recipients = [] - if article.sender.name is 'Agent' - if article.to - localRecipients = emailAddresses.parseAddressList(article.to) - if localRecipients - recipients = recipients.concat localRecipients - else + if article.sender.name is 'Customer' if article.from localRecipients = emailAddresses.parseAddressList(article.from) if localRecipients recipients = recipients.concat localRecipients + if article.to + localRecipients = emailAddresses.parseAddressList(article.to) + if localRecipients + recipients = recipients.concat localRecipients if article.cc localRecipients = emailAddresses.parseAddressList(article.cc) if localRecipients recipients = recipients.concat localRecipients - if recipients.length > 1 + + # remove system addresses + localAddresses = App.EmailAddress.all() + forgeinRecipients = [] + recipientUsed = {} + for recipient in recipients + localRecipientAddeess = recipient.address.toString().toLowerCase() + if !recipientUsed[localRecipientAddeess] + recipientUsed[localRecipientAddeess] = true + localAddess = false + for address in localAddresses + if localRecipientAddeess is address.email.toString().toLowerCase() + recipientUsed[localRecipientAddeess] = true + localAddess = true + if !localAddess + forgeinRecipients.push recipient + + # check if reply all is neede + if forgeinRecipients.length > 1 actions.push { name: 'reply all' type: 'emailReplyAll' diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee index da24eeaa8..6dda62756 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee @@ -100,7 +100,7 @@ class ArticleViewItem extends App.Controller # set @el attributes if !article @el.addClass("ticket-article-item #{@article.sender.name.toLowerCase()}") - @el.attr('data-id', @article.id) + @el.attr('data-id', @article.id) @el.attr('id', "article-#{@article.id}") # set internal change directly in dom, without rerender while article @@ -166,7 +166,7 @@ class ArticleViewItem extends App.Controller @shown = false a = => @setSeeMore() - @delay( a, 50 ) + @delay(a, 50) # set highlighter @setHighlighter() diff --git a/app/assets/javascripts/app/controllers/trigger.coffee b/app/assets/javascripts/app/controllers/trigger.coffee index 123f79965..4376f2d6f 100644 --- a/app/assets/javascripts/app/controllers/trigger.coffee +++ b/app/assets/javascripts/app/controllers/trigger.coffee @@ -1,88 +1,30 @@ -class Index extends App.ControllerTabs - header: 'Trigger' +class Index extends App.ControllerContent constructor: -> super - @title 'Trigger', true + # check authentication + return if !@authenticate(false, 'Admin') - @tabs = [ - { - name: 'Time Based', - target: 'c-time-based', - controller: App.TriggerTime, - }, - { - name: 'Event Based', - target: 'c-event-based', - controller: App.SettingsArea, - params: { area: 'Email::Base' }, - }, - { - name: 'Notifications', - target: 'c-notification', - controller: App.SettingsArea, - params: { area: 'Email::Base' }, - }, - { - name: 'Web Hooks', - target: 'c-web-hook', - controller: App.SettingsArea, - params: { area: 'Email::Base' }, - }, - ] - - @render() - -App.Config.set( 'Trigger', { prio: 3000, name: 'Trigger', parent: '#manage', target: '#manage/triggers', controller: Index, role: ['Admin'] }, 'NavBarAdmin' ) - -class App.TriggerTime extends App.Controller - events: - 'click .js-new': 'new' - #'click .js-edit': 'edit' - 'click .js-delete': 'delete' - - constructor: -> - super - @interval(@load, 30000) - #@load() - - load: => - @startLoading() - @ajax( - id: 'trigger_time_index' - type: 'GET' - url: @apiPath + '/jobs' - processData: true - success: (data, status, xhr) => - #App.Collection.loadAssets(data.assets) - @stopLoading() - @render(data) - ) - - render: (data = {}) => - - @html App.view('trigger/time/index')( - triggers: [] - ) - - - delete: (e) => - e.preventDefault() - id = $(e.target).closest('.action').data('id') - item = App.Channel.find(id) - new App.ControllerGenericDestroyConfirm( - item: item - container: @el.closest('.content') - callback: @load - ) - - new: (e) => - e.preventDefault() - channel_id = $(e.target).closest('.action').data('id') - new App.ControllerGenericNew( + new App.ControllerGenericIndex( + el: @el + id: @id + genericObject: 'Trigger' + defaultSortBy: 'name' + #groupBy: 'role' pageData: - object: 'Jobs' - genericObject: 'Job' + title: 'Triggers' + home: 'triggers' + object: 'Trigger' + objects: 'Triggers' + navupdate: '#triggers' + notes: [ + 'Triggers are ...' + ] + buttons: [ + { name: 'New Trigger', 'data-type': 'new', class: 'btn--success' } + ] container: @el.closest('.content') - callback: @load + #large: true ) + +App.Config.set('Trigger', { prio: 3300, name: 'Trigger', parent: '#manage', target: '#manage/trigger', controller: Index, role: ['Admin'] }, 'NavBarAdmin') diff --git a/app/assets/javascripts/app/lib/app_post/websocket.coffee b/app/assets/javascripts/app/lib/app_post/websocket.coffee index 8795e66a4..6664a52f1 100644 --- a/app/assets/javascripts/app/lib/app_post/websocket.coffee +++ b/app/assets/javascripts/app/lib/app_post/websocket.coffee @@ -98,10 +98,10 @@ class _webSocketSingleton extends App.Controller # send ping after visibilitychange to check if connection is open again after wakeup $(document).bind('visibilitychange', => - console.log('visibilitychange') + @log 'debug', 'visibilitychange' return if document.hidden return if !@connectionEstablished - console.log('ping') + @log 'debug', 'ping' @ping() ) diff --git a/app/assets/javascripts/app/models/job.coffee b/app/assets/javascripts/app/models/job.coffee index 855408151..36fd9611b 100644 --- a/app/assets/javascripts/app/models/job.coffee +++ b/app/assets/javascripts/app/models/job.coffee @@ -1,27 +1,30 @@ class App.Job extends App.Model - @configure 'Job', 'name', 'timeplan', 'condition', 'execute', 'note', 'active' + @configure 'Job', 'name', 'timeplan', 'condition', 'perform', 'disable_notiifcation', 'note', 'active' @extend Spine.Model.Ajax @url: @apiPath + '/jobs' @configure_attributes = [ - { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false }, -# { name: 'timeplan', display: 'The times where the job should run.', tag: 'timeplan', null: true }, - { name: 'condition', display: 'Conditions for matching objects.', tag: 'ticket_selector', null: true }, - { name: 'execute', display: 'Execute changes on objects.', tag: 'ticket_perform_action', null: true }, - { name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true }, - { name: 'active', display: 'Active', tag: 'active', default: true }, - { name: 'matching', display: 'Matching', readonly: 1 }, - { name: 'processed', display: 'Processed', readonly: 1 }, - { name: 'last_run_at', display: 'Last run', tag: 'datetime', readonly: 1 }, - { name: 'running', display: 'Running', tag: 'boolean', readonly: 1 }, - { name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 }, - { name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 }, - { name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 }, - { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }, + { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false }, + { name: 'timeplan', display: 'When should the job run?', tag: 'timer', null: true }, + { name: 'condition', display: 'Conditions for effected objects', tag: 'ticket_selector', null: true }, + { name: 'perform', display: 'Execute changes on objects', tag: 'ticket_perform_action', null: true }, + { name: 'disable_notiifcation', display: 'Disable Notifications', tag: 'boolean', default: true }, + { name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true }, + { name: 'active', display: 'Active', tag: 'active', default: true }, + { name: 'matching', display: 'Will process', readonly: 1 }, + { name: 'processed', display: 'Has processed', readonly: 1 }, + { name: 'last_run_at', display: 'Last run', tag: 'datetime', readonly: 1 }, + { name: 'next_run_at', display: 'Scheduled for', tag: 'datetime', readonly: 1 }, + { name: 'running', display: 'Running', tag: 'boolean', readonly: 1 }, + { name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 }, + { name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 }, + { name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 }, + { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }, ] @configure_delete = true @configure_overview = [ 'name', 'last_run_at', - 'matching', 'processed', + 'next_run_at', + 'matching', ] diff --git a/app/assets/javascripts/app/models/trigger.coffee b/app/assets/javascripts/app/models/trigger.coffee new file mode 100644 index 000000000..2c9352d60 --- /dev/null +++ b/app/assets/javascripts/app/models/trigger.coffee @@ -0,0 +1,21 @@ +class App.Trigger extends App.Model + @configure 'Trigger', 'name', 'condition', 'perform', 'active' + @extend Spine.Model.Ajax + @url: @apiPath + '/triggers' + @configure_attributes = [ + { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false }, + { name: 'condition', display: 'Conditions for effected objects', tag: 'ticket_selector', null: false }, + { name: 'perform', display: 'Execute changes on objects', tag: 'ticket_perform_action', null: true }, + { name: 'disable_notiifcation', display: 'Disable Notifications', tag: 'boolean', default: true }, + { name: 'active', display: 'Active', tag: 'active', default: true }, + { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }, + ] + @configure_delete = true + @configure_overview = [ + 'name', + ] + + @description = ''' +Trigger are.... + +''' \ No newline at end of file diff --git a/app/assets/javascripts/app/views/generic/timer.jst.eco b/app/assets/javascripts/app/views/generic/timer.jst.eco new file mode 100644 index 000000000..fd531f5fa --- /dev/null +++ b/app/assets/javascripts/app/views/generic/timer.jst.eco @@ -0,0 +1,26 @@ +

+ +

+
+
+
<%- @T('Day') %>
+ <% for day, dayLong of @days: %> +
<%- @T(dayLong) %>
+ + <% end %> +
+
+
<%- @T('Hour') %>
+ <% for hour, hourLong of @hours: %> +
<%- hourLong %>
+ + <% end %> +
+
+
<%- @T('Minute') %>
+ <% for minute, minuteLong of @minutes: %> +
<%- minuteLong %>
+ + <% end %> +
+
\ No newline at end of file diff --git a/app/controllers/triggers_controller.rb b/app/controllers/triggers_controller.rb new file mode 100644 index 000000000..867f67454 --- /dev/null +++ b/app/controllers/triggers_controller.rb @@ -0,0 +1,30 @@ +# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ + +class TriggersController < ApplicationController + before_action :authentication_check + + def index + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_index_render(Trigger, params) + end + + def show + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_show_render(Trigger, params) + end + + def create + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_create_render(Trigger, params) + end + + def update + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_update_render(Trigger, params) + end + + def destroy + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_destory_render(Trigger, params) + end +end diff --git a/app/models/application_model.rb b/app/models/application_model.rb index eefa7ef27..28e0c17a8 100644 --- a/app/models/application_model.rb +++ b/app/models/application_model.rb @@ -92,7 +92,7 @@ returns =begin -set rellations of model based on params +set relations of model based on params model = Model.find(1) result = model.param_set_associations(params) @@ -117,6 +117,7 @@ returns end list = [] list_of_items.each {|item| + next if !item list.push(assoc.klass.find(item)) } send(assoc.name.to_s + '=', list) diff --git a/app/models/channel/email_parser.rb b/app/models/channel/email_parser.rb index 15e8a6fe4..e449ea0b5 100644 --- a/app/models/channel/email_parser.rb +++ b/app/models/channel/email_parser.rb @@ -507,7 +507,7 @@ retrns end end - # execute ticket events + # execute ticket notification events Observer::Ticket::Notification.transaction # run postmaster post filter diff --git a/app/models/job.rb b/app/models/job.rb index 352e71972..7d4a66bf7 100644 --- a/app/models/job.rb +++ b/app/models/job.rb @@ -3,85 +3,233 @@ class Job < ApplicationModel store :timeplan store :condition - store :execute + store :perform validates :name, presence: true - before_create :updated_matching - before_update :updated_matching + before_create :updated_matching, :update_next_run_at + before_update :updated_matching, :update_next_run_at notify_clients_support def self.run - time = Time.zone.now - day_map = { - 0 => 'sun', - 1 => 'mon', - 2 => 'tue', - 3 => 'wed', - 4 => 'thu', - 5 => 'fri', - 6 => 'sat', - } - jobs = Job.where( active: true ) + jobs = Job.where(active: true, running: false) jobs.each do |job| + logger.debug "Execute job #{job.inspect}" - # only execute jobs, older then 1 min, to give admin posibility to change - next if job.updated_at > Time.zone.now - 1.minute + next if !job.executable? - # check if jobs need to be executed - # ignore if job was running within last 10 min. - next if job.last_run_at && job.last_run_at > Time.zone.now - 10.minutes - - # check day - next if !job.timeplan['days'].include?( day_map[time.wday] ) - - # check hour - next if !job.timeplan['hours'].include?( time.hour.to_s ) - - # check min - next if !job.timeplan['minutes'].include?( match_minutes(time.min.to_s) ) - - # find tickets to change - tickets = Ticket.where( job.condition.permit! ) - .order( '`tickets`.`created_at` DESC' ) - .limit( 1_000 ) - job.processed = tickets.count - tickets.each do |ticket| - logger.debug "CHANGE #{job.execute.inspect}" - changed = false - job.execute.each do |key, value| - changed = true - attribute = key.split('.', 2).last - logger.debug "-- #{Ticket.columns_hash[ attribute ].type}" - #value = 4 - #if Ticket.columns_hash[ attribute ].type == :integer - # logger.debug "to i #{attribute}/#{value.inspect}/#{value.to_i.inspect}" - # #value = value.to_i - #end - ticket[attribute] = value - logger.debug "set #{attribute} = #{value.inspect}" - end - next if !changed - ticket.updated_by_id = 1 - ticket.save + matching = job.matching_count + if job.matching != matching + job.matching = matching + job.save end + next if !job.in_timeplan? + + # find tickets to change + ticket_count, tickets = Ticket.selectors(job.condition, 2_000) + + logger.debug "Job #{job.name} with #{ticket_count} tickets" + + job.processed = ticket_count || 0 + job.running = true + job.save + + if tickets + tickets.each do |ticket| + + # use transaction + ActiveRecord::Base.transaction do + UserInfo.current_user_id = 1 + + logger.debug "Perform job #{job.perform.inspect} in Ticket.find(#{ticket.id})" + changed = false + job.perform.each do |key, value| + (object_name, attribute) = key.split('.', 2) + raise "Unable to update object #{object_name}.#{attribute}, only can update tickets!" if object_name != 'ticket' + + next if ticket[attribute].to_s == value['value'].to_s + changed = true + + ticket[attribute] = value['value'] + logger.debug "set #{object_name}.#{attribute} = #{value['value'].inspect}" + end + next if !changed + ticket.save + + # execute ticket notification events + if !job.disable_notification + Observer::Ticket::Notification.transaction + end + end + end + end + + job.running = false job.last_run_at = Time.zone.now job.save end true end + def executable? + return false if !active + + # only execute jobs, older then 1 min, to give admin posibility to change + return false if updated_at > Time.zone.now - 1.minute + + # check if jobs need to be executed + # ignore if job was running within last 10 min. + return false if last_run_at && last_run_at > Time.zone.now - 10.minutes + + true + end + + def in_timeplan?(time = Time.zone.now) + day_map = { + 0 => 'Sun', + 1 => 'Mon', + 2 => 'Tue', + 3 => 'Wed', + 4 => 'Thu', + 5 => 'Fri', + 6 => 'Sat', + } + + # check day + return false if !timeplan['days'] + return false if !timeplan['days'][day_map[time.wday]] + + # check hour + return false if !timeplan['hours'] + return false if !timeplan['hours'][time.hour.to_s] && !timeplan['hours'][time.hour] + + # check min + return false if !timeplan['minutes'] + return false if !timeplan['minutes'][match_minutes(time.min).to_s] && !timeplan['minutes'][match_minutes(time.min)] + + true + end + + def matching_count + ticket_count, tickets = Ticket.selectors(condition, 1) + ticket_count || 0 + end + + def next_run_at_calculate(time = Time.zone.now) + if last_run_at + diff = time - last_run_at + if diff > 0 + time = time + 10.minutes + end + end + day_map = { + 0 => 'Sun', + 1 => 'Mon', + 2 => 'Tue', + 3 => 'Wed', + 4 => 'Thu', + 5 => 'Fri', + 6 => 'Sat', + } + return nil if !active + return nil if !timeplan['days'] + return nil if !timeplan['hours'] + return nil if !timeplan['minutes'] + + # loop week days + (0..7).each do |day_counter| + time_to_check = nil + day_to_check = if day_counter == 0 + time + else + time + 1.day + end + if !timeplan['days'][day_map[day_to_check.wday]] + + # start on next day at 00:00:00 + time = day_to_check - day_to_check.sec.seconds + time = time - day_to_check.min.minutes + time = time - day_to_check.hour.hours + next + end + + min = day_to_check.min + if min < 9 + min = 0 + elsif min < 20 + min = 10 + elsif min < 30 + min = 20 + elsif min < 40 + min = 30 + elsif min < 50 + min = 40 + elsif min < 60 + min = 50 + end + + # move to [0-5]0:00 time stamps + day_to_check = day_to_check - day_to_check.min.minutes + min.minutes + day_to_check = day_to_check - day_to_check.sec.seconds + + # loop minutes till next full hour + if day_to_check.min != 0 + (0..5).each do |minute_counter| + if minute_counter != 0 + break if day_to_check.min == 0 + day_to_check = day_to_check + 10.minutes + end + next if !timeplan['hours'][day_to_check.hour] && !timeplan['hours'][day_to_check.hour.to_s] + next if !timeplan['minutes'][match_minutes(day_to_check.min)] && !timeplan['minutes'][match_minutes(day_to_check.min).to_s] + return day_to_check + end + end + + # loop hours + hour_to_check = nil + (0..23).each do |hour_counter| + hour_to_check = day_to_check + hour_counter.hours + + # start on next day + if hour_to_check.day != day_to_check.day + time = day_to_check - day_to_check.hour.hours + break + end + + # ignore not configured hours + next if !timeplan['hours'][hour_to_check.hour] && !timeplan['hours'][hour_to_check.hour.to_s] + return nil if !hour_to_check + + # loop minutes + minute_to_check = nil + (0..5).each do |minute_counter| + minute_to_check = hour_to_check + minute_counter.minutes * 10 + next if !timeplan['minutes'][match_minutes(minute_to_check.min)] && !timeplan['minutes'][match_minutes(minute_to_check.min).to_s] + time_to_check = minute_to_check + break + end + next if !minute_to_check + return time_to_check + end + + end + nil + end + private def updated_matching - count = Ticket.where( condition.permit! ).count - self.matching = count + self.matching = matching_count end - def self.match_minutes(minutes) - minutes.gsub!(/(\d)\d/, '\\1') - minutes.to_s + '0' + def update_next_run_at + self.next_run_at = next_run_at_calculate end - private_class_method :match_minutes + + def match_minutes(minutes) + return 0 if minutes < 10 + "#{minutes.to_s.gsub(/(\d)\d/, '\\1')}0".to_i + end + end diff --git a/app/models/ticket.rb b/app/models/ticket.rb index 68448d6b9..b89d8b317 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -468,7 +468,7 @@ condition example raise "Invalid selector, operator missing #{selector.inspect}" if !selector['operator'] # validate value / allow empty but only if pre_condition exists - if (selector['value'].class == String || selector['value'].class == Array) && (selector['value'].respond_to?(:empty?) && selector['value'].empty?) + if !selector.key?('value') || ((selector['value'].class == String || selector['value'].class == Array) && (selector['value'].respond_to?(:empty?) && selector['value'].empty?)) return nil if selector['pre_condition'].nil? || (selector['pre_condition'].respond_to?(:empty?) && selector['pre_condition'].empty?) end @@ -679,7 +679,7 @@ result return if !customer_id - customer = User.find( customer_id ) + customer = User.find(customer_id) return if organization_id == customer.organization_id self.organization_id = customer.organization_id @@ -691,8 +691,8 @@ result return if !changes['state_id'] # check if new state isn't pending* - current_state = Ticket::State.lookup( id: state_id ) - current_state_type = Ticket::StateType.lookup( id: current_state.state_type_id ) + current_state = Ticket::State.lookup(id: state_id) + current_state_type = Ticket::StateType.lookup(id: current_state.state_type_id) # in case, set pending_time to nil return if current_state_type.name =~ /^pending/i @@ -706,7 +706,7 @@ result articles.destroy_all # destroy online notifications - OnlineNotification.remove( self.class.to_s, id ) + OnlineNotification.remove(self.class.to_s, id) end end diff --git a/app/models/ticket/number/date.rb b/app/models/ticket/number/date.rb index 48884dc2f..8e57f5b65 100644 --- a/app/models/ticket/number/date.rb +++ b/app/models/ticket/number/date.rb @@ -13,9 +13,9 @@ module Ticket::Number::Date # read counter counter_increment = nil Ticket::Counter.transaction do - counter = Ticket::Counter.where( generator: 'Date' ).lock(true).first + counter = Ticket::Counter.where(generator: 'Date').lock(true).first if !counter - counter = Ticket::Counter.new( generator: 'Date', content: '0' ) + counter = Ticket::Counter.new(generator: 'Date', content: '0') end # increase counter @@ -48,7 +48,7 @@ module Ticket::Number::Date mult = 1 (1..number.length).each do |i| digit = number.to_s[i, 1] - chksum = chksum + ( mult * digit.to_i ) + chksum = chksum + (mult * digit.to_i) mult += 1 if mult == 3 mult = 1 @@ -65,6 +65,7 @@ module Ticket::Number::Date end def check(string) + return if !string || string.empty? # get config system_id = Setting.get('system_id') || '' @@ -73,10 +74,15 @@ module Ticket::Number::Date ticket = nil # probe format - if string =~ /#{ticket_hook}#{ticket_hook_divider}(#{system_id}\d{2,50})/i - ticket = Ticket.find_by( number: $1 ) - elsif string =~ /#{ticket_hook}\s{0,2}(#{system_id}\d{2,50})/i - ticket = Ticket.find_by( number: $1 ) + string.scan(/#{ticket_hook}#{ticket_hook_divider}(#{system_id}\d{2,48})/i) { + ticket = Ticket.find_by(number: $1) + break if ticket + } + if !ticket + string.scan(/#{ticket_hook}\s{0,2}(#{system_id}\d{2,48})/i) { + ticket = Ticket.find_by(number: $1) + break if ticket + } end ticket end diff --git a/app/models/ticket/number/increment.rb b/app/models/ticket/number/increment.rb index 31f464ba9..d268b44d9 100644 --- a/app/models/ticket/number/increment.rb +++ b/app/models/ticket/number/increment.rb @@ -12,9 +12,9 @@ module Ticket::Number::Increment min_digs = config[:min_size] || 4 counter_increment = nil Ticket::Counter.transaction do - counter = Ticket::Counter.where( generator: 'Increment' ).lock(true).first + counter = Ticket::Counter.where(generator: 'Increment').lock(true).first if !counter - counter = Ticket::Counter.new( generator: 'Increment', content: '0' ) + counter = Ticket::Counter.new(generator: 'Increment', content: '0') end counter_increment = counter.content.to_i @@ -31,9 +31,9 @@ module Ticket::Number::Increment min_digs = min_digs.to_i - 1 end fillup = Setting.get('system_id').to_s || '1' - ( 1..100 ).each { + (1..100).each { - next if ( fillup.length.to_i + counter_increment.to_s.length.to_i ) >= min_digs.to_i + next if (fillup.length.to_i + counter_increment.to_s.length.to_i) >= min_digs.to_i fillup = fillup + '0' } @@ -53,7 +53,7 @@ module Ticket::Number::Increment mult = 1 (1..number.length).each do |i| digit = number.to_s[i, 1] - chksum = chksum + ( mult * digit.to_i ) + chksum = chksum + (mult * digit.to_i) mult += 1 if mult == 3 mult = 1 @@ -70,6 +70,7 @@ module Ticket::Number::Increment end def check(string) + return if !string || string.empty? # get config system_id = Setting.get('system_id') || '' @@ -78,10 +79,15 @@ module Ticket::Number::Increment ticket = nil # probe format - if string =~ /#{ticket_hook}#{ticket_hook_divider}(#{system_id}\d{2,48})/i - ticket = Ticket.find_by( number: $1 ) - elsif string =~ /#{ticket_hook}\s{0,2}(#{system_id}\d{2,48})/i - ticket = Ticket.find_by( number: $1 ) + string.scan(/#{ticket_hook}#{ticket_hook_divider}(#{system_id}\d{2,48})/i) { + ticket = Ticket.find_by(number: $1) + break if ticket + } + if !ticket + string.scan(/#{ticket_hook}\s{0,2}(#{system_id}\d{2,48})/i) { + ticket = Ticket.find_by(number: $1) + break if ticket + } end ticket end diff --git a/app/views/tests/form_timer.html.erb b/app/views/tests/form_timer.html.erb new file mode 100644 index 000000000..5e9a1eaad --- /dev/null +++ b/app/views/tests/form_timer.html.erb @@ -0,0 +1,22 @@ + + + + + + + + + +
+ +
+
+
+ +
+
diff --git a/config/application.rb b/config/application.rb index b599993a4..59a17ebcf 100644 --- a/config/application.rb +++ b/config/application.rb @@ -48,7 +48,7 @@ module Zammad config.api_path = '/api/v1' # define cache store - config.cache_store = :file_store, "tmp/cache_file_store_#{Rails.env}" + config.cache_store = :file_store, "#{Rails.root}/tmp/cache_file_store_#{Rails.env}" # default preferences by role config.preferences_default_by_role = { diff --git a/config/environments/production.rb b/config/environments/production.rb index 85a53a8d0..5600b66ed 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -54,9 +54,6 @@ Rails.application.configure do # Use a different logger for distributed setups. # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) - # Use a different cache store in production. - # config.cache_store = :mem_cache_store - # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.action_controller.asset_host = 'http://assets.example.com' @@ -83,7 +80,4 @@ Rails.application.configure do # format log config.log_formatter = Logger::Formatter.new - # define cache store - config.cache_store = :file_store, 'tmp/cache_file_store_production' - end diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 000000000..bfd115475 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,10 @@ +workers Integer(ENV['WEB_CONCURRENCY'] || 0) +threads_count_min = Integer(ENV['MIN_THREADS'] || 5) +threads_count_max = Integer(ENV['MAX_THREADS'] || 30) +threads threads_count_min, threads_count_max + +preload_app! + +on_worker_boot do + ActiveRecord::Base.establish_connection +end diff --git a/config/routes/test.rb b/config/routes/test.rb index 9e2a315ec..639adf2d1 100644 --- a/config/routes/test.rb +++ b/config/routes/test.rb @@ -8,6 +8,7 @@ Zammad::Application.routes.draw do match '/tests_form_find', to: 'tests#form_find', via: :get match '/tests_form_trim', to: 'tests#form_trim', via: :get match '/tests_form_extended', to: 'tests#form_extended', via: :get + match '/tests_form_timer', to: 'tests#form_timer', via: :get match '/tests_form_validation', to: 'tests#form_validation', via: :get match '/tests_form_column_select', to: 'tests#form_column_select', via: :get match '/tests_form_searchable_select', to: 'tests#form_searchable_select', via: :get diff --git a/config/routes/trigger.rb b/config/routes/trigger.rb new file mode 100644 index 000000000..3c34c4d2d --- /dev/null +++ b/config/routes/trigger.rb @@ -0,0 +1,11 @@ +Zammad::Application.routes.draw do + api_path = Rails.configuration.api_path + + # triggers + match api_path + '/triggers', to: 'triggers#index', via: :get + match api_path + '/triggers/:id', to: 'triggers#show', via: :get + match api_path + '/triggers', to: 'triggers#create', via: :post + match api_path + '/triggers/:id', to: 'triggers#update', via: :put + match api_path + '/triggers/:id', to: 'triggers#destroy', via: :delete + +end diff --git a/db/migrate/20120101000010_create_ticket.rb b/db/migrate/20120101000010_create_ticket.rb index 2c0912898..b2359d099 100644 --- a/db/migrate/20120101000010_create_ticket.rb +++ b/db/migrate/20120101000010_create_ticket.rb @@ -188,7 +188,6 @@ class CreateTicket < ActiveRecord::Migration add_index :ticket_counters, [:generator], unique: true create_table :overviews do |t| - t.references :user, null: true t.references :role, null: false t.column :name, :string, limit: 250, null: false t.column :link, :string, limit: 250, null: false @@ -203,9 +202,15 @@ class CreateTicket < ActiveRecord::Migration t.column :created_by_id, :integer, null: false t.timestamps null: false end - add_index :overviews, [:user_id] add_index :overviews, [:name] + create_table :overviews_users, id: false do |t| + t.integer :overview_id + t.integer :user_id + end + add_index :overviews_users, [:overview_id] + add_index :overviews_users, [:user_id] + create_table :overviews_groups, id: false do |t| t.integer :overview_id t.integer :group_id @@ -214,13 +219,37 @@ 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 :key, :string, limit: 250, null: false - t.column :value, :string, limit: 250, null: false + 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.timestamps null: false end - add_index :triggers, [:name] - add_index :triggers, [:key] - add_index :triggers, [:value] + add_index :triggers, [:name], unique: true + + create_table :jobs do |t| + t.column :name, :string, limit: 250, null: false + t.column :timeplan, :string, limit: 500, 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, null: true + t.column :next_run_at, :timestamp, 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 null: false + end + add_index :jobs, [:name], unique: true create_table :notifications do |t| t.column :subject, :string, limit: 250, null: false diff --git a/db/migrate/20141221000001_create_job.rb b/db/migrate/20141221000001_create_job.rb deleted file mode 100644 index 17723d5b5..000000000 --- a/db/migrate/20141221000001_create_job.rb +++ /dev/null @@ -1,25 +0,0 @@ -class CreateJob < ActiveRecord::Migration - def up - create_table :jobs do |t| - t.column :name, :string, limit: 250, null: false - t.column :timeplan, :string, limit: 500, null: false - t.column :condition, :string, limit: 2500, null: false - t.column :execute, :string, limit: 2500, null: false - t.column :last_run_at, :timestamp, 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 null: false - end - add_index :jobs, [:name], unique: true - end - - def down - drop_table :jobs - end -end diff --git a/db/migrate/20160314000001_overview_user_relation.rb b/db/migrate/20160314000001_overview_user_relation.rb deleted file mode 100644 index 5a7adfe07..000000000 --- a/db/migrate/20160314000001_overview_user_relation.rb +++ /dev/null @@ -1,13 +0,0 @@ - -class OverviewUserRelation < ActiveRecord::Migration - def up - create_table :overviews_users, id: false do |t| - t.integer :overview_id - t.integer :user_id - end - add_index :overviews_users, [:overview_id] - add_index :overviews_users, [:user_id] - remove_column :overviews, :user_id - end - -end diff --git a/db/migrate/20160316000006_renew_triggers.rb b/db/migrate/20160316000006_renew_triggers.rb new file mode 100644 index 000000000..5469f2d1e --- /dev/null +++ b/db/migrate/20160316000006_renew_triggers.rb @@ -0,0 +1,49 @@ +class RenewTriggers < ActiveRecord::Migration + def up + drop_table :triggers + 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.timestamps null: false + end + add_index :triggers, [:name], unique: true + + drop_table :jobs + 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, null: true + t.column :next_run_at, :timestamp, null: true + t.column :running, :boolean, null: false, default: false + t.column :processed, :integer, null: false, default: 0 + t.column :matching, :integer, null: false, default: 0 + 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 null: false + end + add_index :jobs, [:name], unique: true + + Scheduler.create_if_not_exists( + name: 'Execute jobs', + method: 'Job.run', + period: 5 * 60, + prio: 2, + active: true, + updated_by_id: 1, + created_by_id: 1, + ) + + end +end diff --git a/db/seeds.rb b/db/seeds.rb index 17abd8e95..05ea1c932 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -3367,6 +3367,15 @@ Scheduler.create_if_not_exists( updated_by_id: 1, created_by_id: 1, ) +Scheduler.create_if_not_exists( + name: 'Execute jobs', + method: 'Job.run', + period: 5 * 60, + prio: 2, + active: true, + updated_by_id: 1, + created_by_id: 1, +) Scheduler.create_if_not_exists( name: 'Cleanup expired sessions', method: 'SessionHelper.cleanup_expired', diff --git a/lib/auto_wizard.rb b/lib/auto_wizard.rb index f69c521f0..8830b5705 100644 --- a/lib/auto_wizard.rb +++ b/lib/auto_wizard.rb @@ -73,7 +73,7 @@ returns # set Settings if auto_wizard_hash['Settings'] auto_wizard_hash['Settings'].each { |setting_data| - Setting.set( setting_data['name'], setting_data['value'] ) + Setting.set(setting_data['name'], setting_data['value']) } end diff --git a/lib/cache.rb b/lib/cache.rb index 69ef8f905..b225c2dd0 100644 --- a/lib/cache.rb +++ b/lib/cache.rb @@ -28,11 +28,7 @@ write a cache if !params[:expires_in] params[:expires_in] = 7.days end - begin - Rails.cache.write(key.to_s, data, params) - rescue => e - Rails.logger.error "NOTICE: #{e.message}" - end + Rails.cache.write(key.to_s, data, params) end =begin diff --git a/public/assets/tests/form_timer.js b/public/assets/tests/form_timer.js new file mode 100644 index 000000000..9650c2637 --- /dev/null +++ b/public/assets/tests/form_timer.js @@ -0,0 +1,214 @@ + +test("form elements check", function() { + + $('#forms').append('

form elements check

') + var el = $('#form1') + var defaults = { + } + new App.ControllerForm({ + el: el, + model: { + configure_attributes: [ + { name: 'input1', display: 'Input1', tag: 'input', type: 'text', limit: 100, null: true, default: defaults['input1'] }, + { name: 'timer_params', display: 'Timer', tag: 'timer', null: false, default: defaults['timer_params'] }, + ] + }, + autofocus: true + }); + + equal('Run every Monday at 00:00', el.find('.js-timerResult').val()) + + var params = App.ControllerForm.params(el) + var test_params = { + input1: '', + timer_params: { + days: { + 'Mon': true, + 'Tue': false, + 'Wed': false, + 'Thu': false, + 'Fri': false, + 'Sat': false, + 'Sun': false, + }, + hours: { + 0: true, + 1: false, + 2: false, + 3: false, + 4: false, + 5: false, + 6: false, + 7: false, + 8: false, + 9: false, + 10: false, + 11: false, + 12: false, + 13: false, + 14: false, + 15: false, + 16: false, + 17: false, + 18: false, + 19: false, + 20: false, + 21: false, + 22: false, + 23: false, + }, + minutes: { + 0: true, + 10: false, + 20: false, + 30: false, + 40: false, + 50: false, + }, + }, + } + deepEqual(params, test_params, 'form param check') + + $('#forms').append('

form elements check

') + var el = $('#form2') + var defaults = { + input1: '123abc', + timer_params: { + days: { + 'Mon': true, + 'Fri': true, + }, + hours: { + 0: true, + 10: true, + 16: true, + }, + minutes: { + 0: true, + 10: true, + 50: true, + }, + }, + } + new App.ControllerForm({ + el: el, + model: { + configure_attributes: [ + { name: 'input1', display: 'Input1', tag: 'input', type: 'text', limit: 100, null: true, default: defaults['input1'] }, + { name: 'timer_params', display: 'Timer', tag: 'timer', null: false, default: defaults['timer_params'] }, + ] + }, + autofocus: true + }); + + equal('Run every Monday and Friday at 00:00, 00:10, 00:50, 10:00, 10:10, 10:50, 16:00, 16:10 and 16:50', el.find('.js-timerResult').val()) + + var params = App.ControllerForm.params(el) + var test_params = { + input1: '123abc', + timer_params: { + days: { + 'Mon': true, + 'Tue': false, + 'Wed': false, + 'Thu': false, + 'Fri': true, + 'Sat': false, + 'Sun': false, + }, + hours: { + 0: true, + 1: false, + 2: false, + 3: false, + 4: false, + 5: false, + 6: false, + 7: false, + 8: false, + 9: false, + 10: true, + 11: false, + 12: false, + 13: false, + 14: false, + 15: false, + 16: true, + 17: false, + 18: false, + 19: false, + 20: false, + 21: false, + 22: false, + 23: false, + }, + minutes: { + 0: true, + 10: true, + 20: false, + 30: false, + 40: false, + 50: true, + }, + }, + } + deepEqual(params, test_params, 'form param check') + + $('#form2 .js-day [data-value="Sat"]').click() + $('#form2 .js-hour [data-value="16"]').click() + $('#form2 .js-minute [data-value="10"]').click() + + equal('Run every Monday, Friday and Saturday at 00:00, 00:50, 10:00 and 10:50', el.find('.js-timerResult').val()) + + var params = App.ControllerForm.params(el) + var test_params = { + input1: '123abc', + timer_params: { + days: { + 'Mon': true, + 'Tue': false, + 'Wed': false, + 'Thu': false, + 'Fri': true, + 'Sat': true, + 'Sun': false, + }, + hours: { + 0: true, + 1: false, + 2: false, + 3: false, + 4: false, + 5: false, + 6: false, + 7: false, + 8: false, + 9: false, + 10: true, + 11: false, + 12: false, + 13: false, + 14: false, + 15: false, + 16: false, + 17: false, + 18: false, + 19: false, + 20: false, + 21: false, + 22: false, + 23: false, + }, + minutes: { + 0: true, + 10: false, + 20: false, + 30: false, + 40: false, + 50: true, + }, + }, + } + deepEqual(params, test_params, 'form param check') + +}); \ No newline at end of file diff --git a/script/build/test_startup.sh b/script/build/test_startup.sh index 825c9c207..f12d731df 100755 --- a/script/build/test_startup.sh +++ b/script/build/test_startup.sh @@ -16,6 +16,6 @@ echo "export IP=$IP" echo "export BROWSER_PORT=$BROWSER_PORT" #rails s puma -d --pid tmp/pids/server.pid --bind 0.0.0.0 --port $APP_PORT -pumactl start --pidfile tmp/pids/server.pid -d -p $APP_PORT -e $RAILS_ENV +puma --pidfile tmp/pids/server.pid -d -p $APP_PORT -e $RAILS_ENV script/websocket-server.rb start -d -p $WS_PORT script/scheduler.rb start diff --git a/script/init-script-normal-user-rvm-fedora b/script/init-script-normal-user-rvm-fedora index 3358e06d2..e8f5eebf9 100755 --- a/script/init-script-normal-user-rvm-fedora +++ b/script/init-script-normal-user-rvm-fedora @@ -63,7 +63,7 @@ start() { echo -n $"Starting ${NAME}/${RAILS_ENV} application server on port: ${APP_PORT}" # $RAILS server -d -p $APP_PORT --pid $APP_PIDFILE &> /dev/null # thin start --threaded -d -p $APP_PORT --pid $APP_PIDFILE - pumactl start --pidfile $APP_PIDFILE -d -p $APP_PORT -e $RAILS_ENV &> /dev/null + puma --pidfile $APP_PIDFILE -d -p $APP_PORT -e $RAILS_ENV &> /dev/null sleep 2 status -p $APP_PIDFILE &> /dev/null && echo_success || echo_failure echo diff --git a/script/local_browser_tests.sh b/script/local_browser_tests.sh index 35bbca3ea..9900a72c7 100755 --- a/script/local_browser_tests.sh +++ b/script/local_browser_tests.sh @@ -38,16 +38,18 @@ rails r "Setting.set('developer_mode', true)" pumactl --pidfile tmp/pids/puma.pid stop script/websocket-server.rb stop -pumactl start --pidfile tmp/pids/puma.pid -d -p 4445 -e $RAILS_ENV +rails s puma -d --pid tmp/pids/puma.pid --bind 0.0.0.0 --port 4445 script/websocket-server.rb start -d script/scheduler.rb start -sleep 15 +sleep 10 #export REMOTE_URL='http://medenhofer:765d0dd4-994b-4e15-9f89-13f3aedeb462@ondemand.saucelabs.com:80/wd/hub' BROWSER_OS='Windows 2012' BROWSER_VERSION=35 BROWSER=firefox #export REMOTE_URL='http://192.168.178.32:4444/wd/hub' #export REMOTE_URL='http://192.168.178.45:4444/wd/hub' -export REMOTE_URL='http://10.0.0.9:4444/wd/hub' +#export REMOTE_URL='http://10.0.0.9:4444/wd/hub' +#export REMOTE_URL='http://10.8.0.22:4449/wd/hub' +export REMOTE_URL='http://localhost:4444/wd/hub' export RAILS_ENV=test @@ -58,10 +60,10 @@ time rake db:create echo "rake db:migrate" time rake db:migrate +#rake test:browser["BROWSER_URL=http://10.8.0.6:3000"] rake test:browser["BROWSER_URL=http://localhost:4445"] #rake test:browser["BROWSER_URL=http://10.0.0.3:4445"] #rake test:browser["BROWSER_URL=http://localhost:4445 BROWSER=chrome"] -#rake test:browser["BROWSER_URL=http://192.168.178.28:4445"] script/scheduler.rb stop script/websocket-server.rb stop diff --git a/test/browser/aab_unit_test.rb b/test/browser/aab_unit_test.rb index 1cc92f5cf..8d54d4756 100644 --- a/test/browser/aab_unit_test.rb +++ b/test/browser/aab_unit_test.rb @@ -59,6 +59,13 @@ class AAbUnitTest < TestCase value: '0', ) + location( url: browser_url + '/tests_form_timer' ) + sleep 4 + match( + css: '.result .failed', + value: '0', + ) + location( url: browser_url + '/tests_form_extended' ) sleep 4 match( diff --git a/test/unit/calendar_subscription_test.rb b/test/unit/calendar_subscription_test.rb index a7d4c799f..e43f46ee7 100644 --- a/test/unit/calendar_subscription_test.rb +++ b/test/unit/calendar_subscription_test.rb @@ -95,7 +95,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase owner_id: agent2.id, state: Ticket::State.lookup(name: 'new'), priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 16:37:00', + created_at: '2016-02-05 16:38:00', updated_by_id: 1, created_by_id: 1, ) @@ -107,7 +107,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase state: Ticket::State.lookup(name: 'pending reminder'), pending_time: '2016-02-07 16:37:00', priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 16:37:00', + created_at: '2016-02-05 16:39:00', updated_by_id: 1, created_by_id: 1, ) @@ -117,9 +117,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase customer_id: customer1.id, owner_id: agent2.id, state: Ticket::State.lookup(name: 'pending reminder'), - pending_time: '2016-02-07 16:37:00', + pending_time: '2016-02-07 16:38:00', priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 16:37:00', + created_at: '2016-02-05 16:40:00', updated_by_id: 1, created_by_id: 1, ) @@ -129,9 +129,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase customer_id: customer1.id, owner_id: agent1.id, state: Ticket::State.lookup(name: 'new'), - escalation_time: '2016-02-07 17:37:00', + escalation_time: '2016-02-07 17:39:00', priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 16:37:00', + created_at: '2016-02-05 16:41:00', updated_by_id: 1, created_by_id: 1, ) @@ -143,7 +143,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase state: Ticket::State.lookup(name: 'new'), escalation_time: '2016-02-07 16:37:00', priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 16:37:00', + created_at: '2016-02-05 16:42:00', updated_by_id: 1, created_by_id: 1, ) @@ -166,7 +166,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase owner_id: 1, state: Ticket::State.lookup(name: 'new'), priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 17:37:00', + created_at: '2016-02-05 17:38:00', updated_by_id: 1, created_by_id: 1, ) @@ -178,7 +178,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase state: Ticket::State.lookup(name: 'pending reminder'), pending_time: '2016-02-08 16:37:00', priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 17:37:00', + created_at: '2016-02-05 17:39:00', updated_by_id: 1, created_by_id: 1, ) @@ -188,9 +188,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase customer_id: customer1.id, owner_id: 1, state: Ticket::State.lookup(name: 'pending reminder'), - pending_time: '2016-02-08 16:37:00', + pending_time: '2016-02-08 16:38:00', priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 17:37:00', + created_at: '2016-02-05 17:40:00', updated_by_id: 1, created_by_id: 1, ) @@ -200,9 +200,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase customer_id: customer1.id, owner_id: 1, state: Ticket::State.lookup(name: 'new'), - escalation_time: '2016-02-08 17:37:00', + escalation_time: '2016-02-08 18:37:00', priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 17:37:00', + created_at: '2016-02-05 17:41:00', updated_by_id: 1, created_by_id: 1, ) @@ -212,9 +212,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase customer_id: customer1.id, owner_id: 1, state: Ticket::State.lookup(name: 'new'), - escalation_time: '2016-02-08 16:37:00', + escalation_time: '2016-02-08 18:38:00', priority: Ticket::Priority.lookup(name: '2 normal'), - created_at: '2016-02-05 17:37:00', + created_at: '2016-02-05 17:42:00', updated_by_id: 1, created_by_id: 1, ) @@ -230,13 +230,13 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase assert_equal(cal.events.count, 4) assert_equal(cal.events[0].dtstart, Time.zone.today) - assert_equal(cal.events[0].summary, 'new ticket: \'some title1 - new - group_calendar\'') - assert_equal(cal.events[0].description, "T##{ticket1.number}") + assert_equal(cal.events[0].summary, 'new ticket: \'some title1 - escalation - group_calendar\'') + assert_equal(cal.events[0].description, "T##{ticket5.number}") assert_equal(cal.events[0].has_alarm?, false) assert_equal(cal.events[1].dtstart, Time.zone.today) - assert_equal(cal.events[1].summary, 'new ticket: \'some title1 - escalation - group_calendar\'') - assert_equal(cal.events[1].description, "T##{ticket5.number}") + assert_equal(cal.events[1].summary, 'new ticket: \'some title1 - new - group_calendar\'') + assert_equal(cal.events[1].description, "T##{ticket1.number}") assert_equal(cal.events[1].has_alarm?, false) assert_equal(cal.events[2].dtstart, Time.zone.today) @@ -279,23 +279,23 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase assert_equal(cal.events.count, 8) assert_equal(cal.events[0].dtstart, Time.zone.today) - assert_equal(cal.events[0].summary, 'new ticket: \'some title2 - new - group_calendar\'') - assert_equal(cal.events[0].description, "T##{ticket7.number}") + assert_equal(cal.events[0].summary, 'new ticket: \'some title2 - escalation - group_calendar\'') + assert_equal(cal.events[0].description, "T##{ticket11.number}") assert_equal(cal.events[0].has_alarm?, false) assert_equal(cal.events[1].dtstart, Time.zone.today) - assert_equal(cal.events[1].summary, 'new ticket: \'some title2 - escalation - group_calendar\'') - assert_equal(cal.events[1].description, "T##{ticket11.number}") + assert_equal(cal.events[1].summary, 'new ticket: \'some title2 - new - group_calendar\'') + assert_equal(cal.events[1].description, "T##{ticket7.number}") assert_equal(cal.events[1].has_alarm?, false) assert_equal(cal.events[2].dtstart, Time.zone.today) - assert_equal(cal.events[2].summary, 'new ticket: \'some title1 - new - group_calendar\'') - assert_equal(cal.events[2].description, "T##{ticket1.number}") + assert_equal(cal.events[2].summary, 'new ticket: \'some title1 - escalation - group_calendar\'') + assert_equal(cal.events[2].description, "T##{ticket5.number}") assert_equal(cal.events[2].has_alarm?, false) assert_equal(cal.events[3].dtstart, Time.zone.today) - assert_equal(cal.events[3].summary, 'new ticket: \'some title1 - escalation - group_calendar\'') - assert_equal(cal.events[3].description, "T##{ticket5.number}") + assert_equal(cal.events[3].summary, 'new ticket: \'some title1 - new - group_calendar\'') + assert_equal(cal.events[3].description, "T##{ticket1.number}") assert_equal(cal.events[3].has_alarm?, false) assert_equal(cal.events[4].dtstart, Time.zone.today) @@ -329,12 +329,12 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase assert_equal(cal.events.count, 4) assert_equal(cal.events[0].dtstart, Time.zone.today) - assert_equal(cal.events[0].summary, 'new ticket: \'some title1 - new - group_default\'') - assert_equal(cal.events[0].description, "T##{ticket2.number}") + assert_equal(cal.events[0].summary, 'new ticket: \'some title1 - escalation - group_default\'') + assert_equal(cal.events[0].description, "T##{ticket6.number}") assert_equal(cal.events[1].dtstart, Time.zone.today) - assert_equal(cal.events[1].summary, 'new ticket: \'some title1 - escalation - group_default\'') - assert_equal(cal.events[1].description, "T##{ticket6.number}") + assert_equal(cal.events[1].summary, 'new ticket: \'some title1 - new - group_default\'') + assert_equal(cal.events[1].description, "T##{ticket2.number}") assert_equal(cal.events[2].dtstart, Time.zone.today) assert_equal(cal.events[2].summary, 'pending reminder ticket: \'some title1 - pending - group_default\' customer: Notification Customer1 (Selector Org)') @@ -374,23 +374,23 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase assert_equal(cal.events.count, 8) assert_equal(cal.events[0].dtstart, Time.zone.today) - assert_equal(cal.events[0].summary, 'new ticket: \'some title2 - new - group_default\'') - assert_equal(cal.events[0].description, "T##{ticket8.number}") + assert_equal(cal.events[0].summary, 'new ticket: \'some title2 - escalation - group_default\'') + assert_equal(cal.events[0].description, "T##{ticket12.number}") assert_equal(cal.events[0].has_alarm?, false) assert_equal(cal.events[1].dtstart, Time.zone.today) - assert_equal(cal.events[1].summary, 'new ticket: \'some title2 - escalation - group_default\'') - assert_equal(cal.events[1].description, "T##{ticket12.number}") + assert_equal(cal.events[1].summary, 'new ticket: \'some title2 - new - group_default\'') + assert_equal(cal.events[1].description, "T##{ticket8.number}") assert_equal(cal.events[1].has_alarm?, false) assert_equal(cal.events[2].dtstart, Time.zone.today) - assert_equal(cal.events[2].summary, 'new ticket: \'some title1 - new - group_default\'') - assert_equal(cal.events[2].description, "T##{ticket2.number}") - assert_equal(cal.events[1].has_alarm?, false) + assert_equal(cal.events[2].summary, 'new ticket: \'some title1 - escalation - group_default\'') + assert_equal(cal.events[2].description, "T##{ticket6.number}") + assert_equal(cal.events[2].has_alarm?, false) assert_equal(cal.events[3].dtstart, Time.zone.today) - assert_equal(cal.events[3].summary, 'new ticket: \'some title1 - escalation - group_default\'') - assert_equal(cal.events[3].description, "T##{ticket6.number}") + assert_equal(cal.events[3].summary, 'new ticket: \'some title1 - new - group_default\'') + assert_equal(cal.events[3].description, "T##{ticket2.number}") assert_equal(cal.events[3].has_alarm?, false) assert_equal(cal.events[4].dtstart, Time.zone.today) diff --git a/test/unit/email_process_follow_up_test.rb b/test/unit/email_process_follow_up_test.rb index 353c6d8e4..b6e9a1011 100644 --- a/test/unit/email_process_follow_up_test.rb +++ b/test/unit/email_process_follow_up_test.rb @@ -85,45 +85,45 @@ no reference " Setting.set('postmaster_follow_up_search_in', %w(body attachment references)) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_subject) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject) assert_equal(ticket.id, ticket_p.id) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_body) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body) assert_equal(ticket.id, ticket_p.id) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_attachment) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment) assert_equal(ticket.id, ticket_p.id) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_references1) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1) assert_equal(ticket.id, ticket_p.id) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_references2) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2) assert_equal(ticket.id, ticket_p.id) Setting.set('postmaster_follow_up_search_in', setting_orig) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_subject) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject) assert_equal(ticket.id, ticket_p.id) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_body) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body) assert_not_equal(ticket.id, ticket_p.id) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_attachment) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment) assert_not_equal(ticket.id, ticket_p.id) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_references1) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1) assert_not_equal(ticket.id, ticket_p.id) sleep 1 - ticket_p, article_p, user_p = Channel::EmailParser.new.process( {}, email_raw_string_references2) + ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2) assert_not_equal(ticket.id, ticket_p.id) end @@ -165,7 +165,51 @@ Auto-Submitted: auto-replied Some Text" - ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process( {}, email_raw_string) + ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string) + ticket = Ticket.find(ticket.id) + assert_equal(ticket.id, ticket_p.id) + assert_equal('open', ticket.state.name) + end + + test 'process with follow up check - email with more forgein T#\'s in subject' do + + ticket = Ticket.create( + title: 'email with more forgein T#\'s in subject', + group: Group.lookup(name: 'Users'), + customer_id: 2, + state: Ticket::State.lookup(name: 'closed'), + priority: Ticket::Priority.lookup(name: '2 normal'), + updated_by_id: 1, + created_by_id: 1, + ) + article = Ticket::Article.create( + ticket_id: ticket.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'follow up with references follow up check', + message_id: '<20151222145601.30.608881@edenhofer.zammad.com>', + body: 'some message with references follow up check', + internal: false, + sender: Ticket::Article::Sender.lookup(name: 'Agent'), + type: Ticket::Article::Type.lookup(name: 'email'), + updated_by_id: 1, + created_by_id: 1, + ) + sleep 1 + + system_id = Setting.get('system_id') + ticket_hook = Setting.get('ticket_hook') + ticket_hook_divider = Setting.get('ticket_hook_divider') + + tn = "[#{ticket_hook}#{ticket_hook_divider}#{system_id}#{Ticket::Number.generate}99]" + + email_raw_string_subject = "From: me@example.com +To: customer@example.com +Subject: First foreign Tn #{tn} #{tn} #{tn} - #{ticket.subject_build('some new subject')} + +Some Text" + + ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string_subject) ticket = Ticket.find(ticket.id) assert_equal(ticket.id, ticket_p.id) assert_equal('open', ticket.state.name) diff --git a/test/unit/job_test.rb b/test/unit/job_test.rb new file mode 100644 index 000000000..63c8c1b13 --- /dev/null +++ b/test/unit/job_test.rb @@ -0,0 +1,725 @@ +# encoding: utf-8 +require 'test_helper' + +class JobTest < ActiveSupport::TestCase + test 'case 1' do + + # create ticket + group1 = Group.lookup(name: 'Users') + group2 = Group.create_or_update( + name: 'JobTest2', + updated_by_id: 1, + created_by_id: 1, + ) + ticket1 = Ticket.create( + title: 'job test 1', + group: group1, + customer_id: 2, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: Time.zone.now - 3.days, + updated_at: Time.zone.now - 3.days, + created_by_id: 1, + updated_by_id: 1, + ) + ticket2 = Ticket.create( + title: 'job test 2', + group: group1, + customer_id: 2, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: Time.zone.now - 1.day, + created_by_id: 1, + updated_at: Time.zone.now - 1.day, + updated_by_id: 1, + ) + ticket3 = Ticket.create( + title: 'job test 3', + group: group2, + customer_id: 2, + state: Ticket::State.lookup(name: 'open'), + priority: Ticket::Priority.lookup(name: '3 high'), + created_at: Time.zone.now - 1.day, + created_by_id: 1, + updated_at: Time.zone.now - 1.day, + updated_by_id: 1, + ) + ticket4 = Ticket.create( + title: 'job test 4', + group: group2, + customer_id: 2, + state: Ticket::State.lookup(name: 'closed'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: Time.zone.now - 3.days, + created_by_id: 1, + updated_at: Time.zone.now - 3.days, + updated_by_id: 1, + ) + ticket5 = Ticket.create( + title: 'job test 5', + group: group2, + customer_id: 2, + state: Ticket::State.lookup(name: 'open'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: Time.zone.now - 3.days, + created_by_id: 1, + updated_by_id: 1, + updated_at: Time.zone.now - 3.days, + ) + + # create jobs + job1 = Job.create_or_update( + name: 'Test Job1', + timeplan: { + days: { + Mon: false, + Tue: false, + Wed: false, + Thu: false, + Fri: false, + Sat: false, + Sun: false, + }, + hours: { + 0 => false, + 1 => false, + 2 => false, + 3 => false, + 4 => false, + 5 => false, + 6 => false, + 7 => false, + 8 => false, + 9 => false, + 10 => false, + 11 => false, + 12 => false, + 13 => false, + 14 => false, + 15 => false, + 16 => false, + 17 => false, + 18 => false, + 19 => false, + 20 => false, + 21 => false, + 22 => false, + 23 => false, + }, + minutes: { + 0 => false, + 10 => false, + 20 => false, + 30 => false, + 40 => false, + 50 => false, + }, + }, + condition: { + 'ticket.state_id' => { 'operator' => 'is', 'value' => [Ticket::State.lookup(name: 'new').id.to_s, Ticket::State.lookup(name: 'open').id.to_s] }, + 'ticket.created_at' => { 'operator' => 'before (relative)', 'value' => '2', 'range' => 'day' }, + }, + perform: { + 'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s } + }, + disable_notification: true, + last_run_at: nil, + active: true, + created_by_id: 1, + created_at: Time.zone.now, + updated_by_id: 1, + updated_at: Time.zone.now, + ) + assert_not(job1.next_run_at) + assert_not(job1.executable?) + + job1.last_run_at = Time.zone.now - 15.minutes + job1.save + assert_not(job1.executable?) + + job1.updated_at = Time.zone.now - 15.minutes + job1.save + assert(job1.executable?) + + job1.active = false + job1.save + assert_not(job1.executable?) + + job1.active = true + job1.save + assert_not(job1.executable?) + + assert_not(job1.in_timeplan?) + time = Time.zone.now + day_map = { + 0 => 'Sun', + 1 => 'Mon', + 2 => 'Tue', + 3 => 'Wed', + 4 => 'Thu', + 5 => 'Fri', + 6 => 'Sat', + } + job1.timeplan['days'][day_map[time.wday]] = true + job1.save + assert_not(job1.in_timeplan?) + job1.timeplan['hours'][time.hour.to_s] = true + job1.save + assert_not(job1.in_timeplan?) + min = time.min + if min < 9 + min = 0 + elsif min < 20 + min = 10 + elsif min < 30 + min = 20 + elsif min < 40 + min = 30 + elsif min < 50 + min = 40 + elsif min < 60 + min = 50 + end + job1.timeplan['minutes'][min.to_s] = true + job1.save + assert(job1.in_timeplan?) + + job1.timeplan['hours'][time.hour] = true + job1.save + + job1.timeplan['minutes'][min] = true + job1.save + assert(job1.in_timeplan?) + + # execute jobs + job1.updated_at = Time.zone.now - 15.minutes + job1.save + Job.run + + assert(job1.next_run_at) + assert(job1.executable?) + assert(job1.in_timeplan?) + + # verify changes on tickets + ticket1_later = Ticket.find(ticket1.id) + assert_equal('closed', ticket1_later.state.name) + assert_not_equal(ticket1.updated_at.to_s, ticket1_later.updated_at.to_s) + + ticket2_later = Ticket.find(ticket2.id) + assert_equal('new', ticket2_later.state.name) + assert_equal(ticket2.updated_at.to_s, ticket2_later.updated_at.to_s) + + ticket3_later = Ticket.find(ticket3.id) + assert_equal('open', ticket3_later.state.name) + assert_equal(ticket3.updated_at.to_s, ticket3_later.updated_at.to_s) + + ticket4_later = Ticket.find(ticket4.id) + assert_equal('closed', ticket4_later.state.name) + assert_equal(ticket4.updated_at.to_s, ticket4_later.updated_at.to_s) + + ticket5_later = Ticket.find(ticket5.id) + assert_equal('closed', ticket5_later.state.name) + assert_not_equal(ticket5.updated_at.to_s, ticket5_later.updated_at.to_s) + + # execute jobs again + job1.updated_at = Time.zone.now - 15.minutes + job1.save + Job.run + + # verify changes on tickets + ticket1_later_next = Ticket.find(ticket1.id) + assert_equal('closed', ticket1_later_next.state.name) + assert_equal(ticket1_later.updated_at.to_s, ticket1_later_next.updated_at.to_s) + + ticket2_later_next = Ticket.find(ticket2.id) + assert_equal('new', ticket2_later_next.state.name) + assert_equal(ticket2_later.updated_at.to_s, ticket2_later_next.updated_at.to_s) + + ticket3_later_next = Ticket.find(ticket3.id) + assert_equal('open', ticket3_later_next.state.name) + assert_equal(ticket3_later.updated_at.to_s, ticket3_later_next.updated_at.to_s) + + ticket4_later_next = Ticket.find(ticket4.id) + assert_equal('closed', ticket4_later_next.state.name) + assert_equal(ticket4_later.updated_at.to_s, ticket4_later_next.updated_at.to_s) + + ticket5_later_next = Ticket.find(ticket5.id) + assert_equal('closed', ticket5_later_next.state.name) + assert_equal(ticket5_later.updated_at.to_s, ticket5_later_next.updated_at.to_s) + + end + + test 'case 2' do + + # create ticket + group1 = Group.lookup(name: 'Users') + group2 = Group.create_or_update( + name: 'JobTest2', + updated_by_id: 1, + created_by_id: 1, + ) + ticket1 = Ticket.create( + title: 'job test 1', + group: group1, + customer_id: 2, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: Time.zone.now - 3.days, + updated_at: Time.zone.now - 3.days, + created_by_id: 1, + updated_by_id: 1, + ) + ticket2 = Ticket.create( + title: 'job test 2', + group: group1, + customer_id: 2, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + created_at: Time.zone.now - 1.day, + created_by_id: 1, + updated_at: Time.zone.now - 1.day, + updated_by_id: 1, + ) + + # create jobs + job1 = Job.create_or_update( + name: 'Test Job1', + timeplan: { + days: { + Mon: true, + Tue: true, + Wed: true, + Thu: true, + Fri: true, + Sat: true, + Sun: true, + }, + hours: { + 0 => true, + 1 => true, + 2 => true, + 3 => true, + 4 => true, + 5 => true, + 6 => true, + 7 => true, + 8 => true, + 9 => true, + 10 => true, + 11 => true, + 12 => true, + 13 => true, + 14 => true, + 15 => true, + 16 => true, + 17 => true, + 18 => true, + 19 => true, + 20 => true, + 21 => true, + 22 => true, + 23 => true, + }, + minutes: { + 0 => true, + 10 => true, + 20 => true, + 30 => true, + 40 => true, + 50 => true, + }, + }, + condition: { + 'ticket.state_id' => { 'operator' => 'is', 'value' => '' }, + 'ticket.created_at' => { 'operator' => 'before (relative)', 'value' => '2', 'range' => 'day' }, + }, + perform: { + 'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s } + }, + disable_notification: true, + last_run_at: nil, + updated_at: Time.zone.now - 15.minutes, + active: true, + updated_by_id: 1, + created_by_id: 1, + ) + assert(job1.executable?) + assert(job1.in_timeplan?) + Job.run + + # verify changes on tickets + ticket1_later = Ticket.find(ticket1.id) + assert_equal('new', ticket1_later.state.name) + assert_equal(ticket1.updated_at.to_s, ticket1_later.updated_at.to_s) + + ticket2_later = Ticket.find(ticket2.id) + assert_equal('new', ticket2_later.state.name) + assert_equal(ticket2.updated_at.to_s, ticket2_later.updated_at.to_s) + + job1 = Job.create_or_update( + name: 'Test Job1', + timeplan: { + days: { + Mon: true, + Tue: true, + Wed: true, + Thu: true, + Fri: true, + Sat: true, + Sun: true, + }, + hours: { + 0 => true, + 1 => true, + 2 => true, + 3 => true, + 4 => true, + 5 => true, + 6 => true, + 7 => true, + 8 => true, + 9 => true, + 10 => true, + 11 => true, + 12 => true, + 13 => true, + 14 => true, + 15 => true, + 16 => true, + 17 => true, + 18 => true, + 19 => true, + 20 => true, + 21 => true, + 22 => true, + 23 => true, + }, + minutes: { + 0 => true, + 10 => true, + 20 => true, + 30 => true, + 40 => true, + 50 => true, + }, + }, + condition: { + 'ticket.state_id' => { 'operator' => 'is' }, + 'ticket.created_at' => { 'operator' => 'before (relative)', 'value' => '2', 'range' => 'day' }, + }, + perform: { + 'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s } + }, + disable_notification: true, + last_run_at: nil, + updated_at: Time.zone.now - 15.minutes, + active: true, + updated_by_id: 1, + created_by_id: 1, + ) + assert(job1.executable?) + assert(job1.in_timeplan?) + Job.run + + # verify changes on tickets + ticket1_later = Ticket.find(ticket1.id) + assert_equal('new', ticket1_later.state.name) + assert_equal(ticket1.updated_at.to_s, ticket1_later.updated_at.to_s) + + ticket2_later = Ticket.find(ticket2.id) + assert_equal('new', ticket2_later.state.name) + assert_equal(ticket2.updated_at.to_s, ticket2_later.updated_at.to_s) + + end + + test 'case 3' do + + # create jobs + job1 = Job.create_or_update( + name: 'Test Job1', + timeplan: { + days: { + Mon: true, + Tue: false, + Wed: false, + Thu: false, + Fri: true, + Sat: false, + Sun: false, + }, + hours: { + 0 => false, + 1 => true, + 2 => false, + 3 => false, + 4 => false, + 5 => false, + 6 => false, + 7 => false, + 8 => false, + 9 => false, + 10 => true, + 11 => false, + 12 => false, + 13 => false, + 14 => false, + 15 => false, + 16 => false, + 17 => false, + 18 => false, + 19 => false, + 20 => false, + 21 => false, + 22 => false, + 23 => false, + }, + minutes: { + 0 => true, + 10 => false, + 20 => false, + 30 => false, + 40 => true, + 50 => false, + }, + }, + condition: { + 'ticket.state_id' => { 'operator' => 'is', 'value' => [Ticket::State.lookup(name: 'new').id.to_s, Ticket::State.lookup(name: 'open').id.to_s] }, + 'ticket.created_at' => { 'operator' => 'before (relative)', 'value' => '2', 'range' => 'day' }, + }, + perform: { + 'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s } + }, + disable_notification: true, + last_run_at: nil, + active: true, + created_by_id: 1, + created_at: Time.zone.now, + updated_by_id: 1, + updated_at: Time.zone.now, + ) + + time_now = Time.zone.parse('2016-03-18 09:17:13 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-18 10:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-18 10:37:13 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-18 10:40:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-17 09:17:13 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-18 01:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-17 11:17:13 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-18 01:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-19 11:17:13 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-21 01:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-22 00:59:59 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-25 01:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-25 00:59:59 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-25 01:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-24 00:59:59 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-25 01:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-24 23:59:59 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-25 01:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-25 01:00:01 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-25 01:00:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-25 01:09:01 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-25 01:40:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-25 01:09:59 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-25 01:40:00 UTC', next_run_at.to_s) + + job1.last_run_at = Time.zone.parse('2016-03-18 10:00:01 UTC') + job1.save + time_now = Time.zone.parse('2016-03-18 10:00:02 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-18 10:40:00 UTC', next_run_at.to_s) + + job1.last_run_at = Time.zone.parse('2016-03-18 10:40:01 UTC') + job1.save + time_now = Time.zone.parse('2016-03-18 10:40:02 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-21 01:00:00 UTC', next_run_at.to_s) + + end + + test 'case 4' do + + # create jobs + job1 = Job.create_or_update( + name: 'Test Job1', + timeplan: { + days: { + Mon: true, + Tue: false, + Wed: false, + Thu: false, + Fri: true, + Sat: false, + Sun: false, + }, + hours: { + 0 => true, + 1 => false, + 2 => false, + 3 => false, + 4 => false, + 5 => false, + 6 => false, + 7 => false, + 8 => false, + 9 => false, + 10 => true, + 11 => false, + 12 => false, + 13 => false, + 14 => false, + 15 => false, + 16 => false, + 17 => false, + 18 => false, + 19 => false, + 20 => false, + 21 => false, + 22 => false, + 23 => false, + }, + minutes: { + 0 => true, + 10 => false, + 20 => false, + 30 => false, + 40 => true, + 50 => false, + }, + }, + condition: { + 'ticket.state_id' => { 'operator' => 'is', 'value' => [Ticket::State.lookup(name: 'new').id.to_s, Ticket::State.lookup(name: 'open').id.to_s] }, + 'ticket.created_at' => { 'operator' => 'before (relative)', 'value' => '2', 'range' => 'day' }, + }, + perform: { + 'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s } + }, + disable_notification: true, + last_run_at: nil, + active: true, + created_by_id: 1, + created_at: Time.zone.now, + updated_by_id: 1, + updated_at: Time.zone.now, + ) + + time_now = Time.zone.parse('2016-03-17 23:51:23 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-18 00:00:00 UTC', next_run_at.to_s) + + job1.last_run_at = Time.zone.parse('2016-03-17 23:45:01 UTC') + job1.save + time_now = Time.zone.parse('2016-03-17 23:51:23 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-18 00:00:00 UTC', next_run_at.to_s) + + job1.last_run_at = Time.zone.parse('2016-03-17 23:59:01 UTC') + job1.save + time_now = Time.zone.parse('2016-03-17 23:59:23 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-18 00:40:00 UTC', next_run_at.to_s) + + time_now = Time.zone.parse('2016-03-17 23:59:23 UTC') + assert_not(job1.in_timeplan?(time_now)) + + time_now = Time.zone.parse('2016-03-18 00:01:23 UTC') + assert(job1.in_timeplan?(time_now)) + + end + + test 'case 5' do + + # create jobs + job1 = Job.create_or_update( + name: 'Test Job1', + timeplan: { + days: { + Mon: true, + Tue: false, + Wed: false, + Thu: false, + Fri: false, + Sat: false, + Sun: false, + }, + hours: { + '0' => true, + '1' => false, + '2' => false, + '3' => false, + '4' => false, + '5' => false, + '6' => false, + '7' => false, + '8' => false, + '9' => false, + '10' => false, + '11' => false, + '12' => false, + '13' => false, + '14' => false, + '15' => false, + '16' => false, + '17' => false, + '18' => false, + '19' => false, + '20' => false, + '21' => false, + '22' => false, + '23' => false, + }, + minutes: { + '0' => true, + '10' => false, + '20' => false, + '30' => false, + '40' => false, + '50' => false, + }, + }, + condition: { + 'ticket.state_id' => { 'operator' => 'is', 'value' => [Ticket::State.lookup(name: 'new').id.to_s, Ticket::State.lookup(name: 'open').id.to_s] }, + 'ticket.created_at' => { 'operator' => 'before (relative)', 'value' => '2', 'range' => 'day' }, + }, + perform: { + 'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s } + }, + disable_notification: true, + last_run_at: nil, + active: true, + created_by_id: 1, + created_at: Time.zone.now, + updated_by_id: 1, + updated_at: Time.zone.now, + ) + + time_now = Time.zone.parse('2016-03-17 23:51:23 UTC') + next_run_at = job1.next_run_at_calculate(time_now) + assert_equal('2016-03-21 00:00:00 UTC', next_run_at.to_s) + + end + +end diff --git a/test/unit/ticket_selector_test.rb b/test/unit/ticket_selector_test.rb index 32d63631e..6e4e69c4e 100644 --- a/test/unit/ticket_selector_test.rb +++ b/test/unit/ticket_selector_test.rb @@ -146,6 +146,86 @@ class TicketSelectorTest < ActiveSupport::TestCase ticket_count, tickets = Ticket.selectors(condition, 10, customer1) assert_equal(ticket_count, 0) + # search matching with empty value / missing key + condition = { + 'ticket.group_id' => { + operator: 'is', + value: group.id, + }, + 'ticket.state_id' => { + operator: 'is', + }, + } + + ticket_count, tickets = Ticket.selectors(condition, 10) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, agent2) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, customer1) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, customer2) + assert_equal(ticket_count, nil) + + # search matching with empty value [] + condition = { + 'ticket.group_id' => { + operator: 'is', + value: group.id, + }, + 'ticket.state_id' => { + operator: 'is', + value: [], + }, + } + + ticket_count, tickets = Ticket.selectors(condition, 10) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, agent2) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, customer1) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, customer2) + assert_equal(ticket_count, nil) + + # search matching with empty value '' + condition = { + 'ticket.group_id' => { + operator: 'is', + value: group.id, + }, + 'ticket.state_id' => { + operator: 'is', + value: '', + }, + } + + ticket_count, tickets = Ticket.selectors(condition, 10) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, agent1) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, agent2) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, customer1) + assert_equal(ticket_count, nil) + + ticket_count, tickets = Ticket.selectors(condition, 10, customer2) + assert_equal(ticket_count, nil) + # search matching condition = { 'ticket.group_id' => {