Merge branch 'develop' of github.com:martini/zammad into develop

This commit is contained in:
Felix Niklas 2016-03-18 17:34:33 +01:00
commit 8685bdb42a
51 changed files with 1926 additions and 400 deletions

View file

@ -40,12 +40,12 @@ job_unit_mysql:
- mysql - mysql
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- rake db:seed - rake db:seed
- rake test:units - rake test:units
- rake test:controllers - rake test:controllers
- rake db:drop
job_unit_postgresql: job_unit_postgresql:
stage: test stage: test
@ -54,12 +54,12 @@ job_unit_postgresql:
- postgresql - postgresql
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- rake db:seed - rake db:seed
- rake test:units - rake test:units
- rake test:controllers - rake test:controllers
- rake db:drop
job_integration_email_helper: job_integration_email_helper:
stage: test stage: test
@ -67,10 +67,10 @@ job_integration_email_helper:
- core - core
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/email_helper_test.rb - ruby -I test/ test/integration/email_helper_test.rb
- rake db:drop
job_integration_twitter: job_integration_twitter:
stage: test stage: test
@ -79,11 +79,12 @@ job_integration_twitter:
- twitter - twitter
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- rake db:seed - rake db:seed
- ruby -I test/ test/integration/twitter_test.rb - ruby -I test/ test/integration/twitter_test.rb
- rake db:drop
allow_failure: true
job_integration_facebook: job_integration_facebook:
stage: test stage: test
@ -91,11 +92,12 @@ job_integration_facebook:
- core - core
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- rake db:seed - rake db:seed
- ruby -I test/ test/integration/facebook_test.rb - ruby -I test/ test/integration/facebook_test.rb
- rake db:drop
allow_failure: true
job_integration_geo_ip: job_integration_geo_ip:
stage: test stage: test
@ -103,10 +105,10 @@ job_integration_geo_ip:
- core - core
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/geo_ip_test.rb - ruby -I test/ test/integration/geo_ip_test.rb
- rake db:drop
job_integration_geo_location: job_integration_geo_location:
stage: test stage: test
@ -114,10 +116,10 @@ job_integration_geo_location:
- core - core
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/geo_location_test.rb - ruby -I test/ test/integration/geo_location_test.rb
- rake db:drop
job_integration_geo_calendar: job_integration_geo_calendar:
stage: test stage: test
@ -125,10 +127,10 @@ job_integration_geo_calendar:
- core - core
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/geo_calendar_test.rb - ruby -I test/ test/integration/geo_calendar_test.rb
- rake db:drop
job_integration_user_agent: job_integration_user_agent:
stage: test stage: test
@ -136,10 +138,10 @@ job_integration_user_agent:
- core - core
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/user_agent_test.rb - ruby -I test/ test/integration/user_agent_test.rb
- rake db:drop
job_integration_es_mysql: job_integration_es_mysql:
stage: test stage: test
@ -150,12 +152,12 @@ job_integration_es_mysql:
- export RAILS_ENV=test - export RAILS_ENV=test
- export ES_INDEX_RAND=true - export ES_INDEX_RAND=true
- export ES_URL="http://localhost:9200" - export ES_URL="http://localhost:9200"
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/elasticsearch_test.rb - ruby -I test/ test/integration/elasticsearch_test.rb
- ruby -I test/ test/controllers/search_controller_test.rb - ruby -I test/ test/controllers/search_controller_test.rb
- ruby -I test/ test/integration/report_test.rb - ruby -I test/ test/integration/report_test.rb
- rake db:drop
job_integration_es_postgresql: job_integration_es_postgresql:
stage: test stage: test
@ -166,12 +168,12 @@ job_integration_es_postgresql:
- export RAILS_ENV=test - export RAILS_ENV=test
- export ES_INDEX_RAND=true - export ES_INDEX_RAND=true
- export ES_URL="http://localhost:9200" - export ES_URL="http://localhost:9200"
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/elasticsearch_test.rb - ruby -I test/ test/integration/elasticsearch_test.rb
- ruby -I test/ test/controllers/search_controller_test.rb - ruby -I test/ test/controllers/search_controller_test.rb
- ruby -I test/ test/integration/report_test.rb - ruby -I test/ test/integration/report_test.rb
- rake db:drop
job_integration_zendesk_mysql: job_integration_zendesk_mysql:
stage: test stage: test
@ -180,10 +182,10 @@ job_integration_zendesk_mysql:
- mysql - mysql
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/zendesk_import_test.rb - ruby -I test/ test/integration/zendesk_import_test.rb
- rake db:drop
job_integration_zendesk_postgresql: job_integration_zendesk_postgresql:
stage: test stage: test
@ -192,10 +194,10 @@ job_integration_zendesk_postgresql:
- postgresql - postgresql
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/zendesk_import_test.rb - ruby -I test/ test/integration/zendesk_import_test.rb
- rake db:drop
job_integration_otrs_5_mysql: job_integration_otrs_5_mysql:
stage: test stage: test
@ -205,10 +207,10 @@ job_integration_otrs_5_mysql:
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- export IMPORT_OTRS_ENDPOINT="http://vz599.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - export IMPORT_OTRS_ENDPOINT="http://vz599.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/otrs_import_test.rb - ruby -I test/ test/integration/otrs_import_test.rb
- rake db:drop
job_integration_otrs_5_postgresql: job_integration_otrs_5_postgresql:
stage: test stage: test
@ -218,10 +220,10 @@ job_integration_otrs_5_postgresql:
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- export IMPORT_OTRS_ENDPOINT="http://vz599.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - export IMPORT_OTRS_ENDPOINT="http://vz599.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/otrs_import_test.rb - ruby -I test/ test/integration/otrs_import_test.rb
- rake db:drop
job_integration_otrs_4: job_integration_otrs_4:
stage: test stage: test
@ -230,10 +232,10 @@ job_integration_otrs_4:
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- export IMPORT_OTRS_ENDPOINT="http://vz383.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - export IMPORT_OTRS_ENDPOINT="http://vz383.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/otrs_import_test.rb - ruby -I test/ test/integration/otrs_import_test.rb
- rake db:drop
job_integration_otrs_33: job_integration_otrs_33:
stage: test stage: test
@ -242,10 +244,10 @@ job_integration_otrs_33:
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- export IMPORT_OTRS_ENDPOINT="http://vz305.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - export IMPORT_OTRS_ENDPOINT="http://vz305.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/otrs_import_test.rb - ruby -I test/ test/integration/otrs_import_test.rb
- rake db:drop
job_integration_otrs_32: job_integration_otrs_32:
stage: test stage: test
@ -254,10 +256,10 @@ job_integration_otrs_32:
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- export IMPORT_OTRS_ENDPOINT="http://vz382.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - export IMPORT_OTRS_ENDPOINT="http://vz382.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/otrs_import_test.rb - ruby -I test/ test/integration/otrs_import_test.rb
- rake db:drop
job_integration_otrs_31: job_integration_otrs_31:
stage: test stage: test
@ -266,10 +268,10 @@ job_integration_otrs_31:
script: script:
- export RAILS_ENV=test - export RAILS_ENV=test
- export IMPORT_OTRS_ENDPOINT="http://vz381.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator" - export IMPORT_OTRS_ENDPOINT="http://vz381.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
- rake db:drop;
- rake db:create - rake db:create
- rake db:migrate - rake db:migrate
- ruby -I test/ test/integration/otrs_import_test.rb - ruby -I test/ test/integration/otrs_import_test.rb
- rake db:drop
job_integration_twitter_ff: job_integration_twitter_ff:
stage: browser stage: browser

View file

@ -2,7 +2,7 @@ source 'https://rubygems.org'
ruby '2.2.3' ruby '2.2.3'
gem 'rails', '4.2.5.2' gem 'rails', '4.2.6'
gem 'rails-observers' gem 'rails-observers'
gem 'activerecord-session_store' gem 'activerecord-session_store'
@ -122,7 +122,7 @@ group :development, :test do
end end
gem 'puma', '< 3.0' gem 'puma'
# load onw gem's # load onw gem's
local_gemfile = File.join(File.dirname(__FILE__), 'Gemfile.local') local_gemfile = File.join(File.dirname(__FILE__), 'Gemfile.local')

View file

@ -1,40 +1,40 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (4.2.5.2) actionmailer (4.2.6)
actionpack (= 4.2.5.2) actionpack (= 4.2.6)
actionview (= 4.2.5.2) actionview (= 4.2.6)
activejob (= 4.2.5.2) activejob (= 4.2.6)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.5.2) actionpack (4.2.6)
actionview (= 4.2.5.2) actionview (= 4.2.6)
activesupport (= 4.2.5.2) activesupport (= 4.2.6)
rack (~> 1.6) rack (~> 1.6)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.5.2) actionview (4.2.6)
activesupport (= 4.2.5.2) activesupport (= 4.2.6)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.5.2) activejob (4.2.6)
activesupport (= 4.2.5.2) activesupport (= 4.2.6)
globalid (>= 0.3.0) globalid (>= 0.3.0)
activemodel (4.2.5.2) activemodel (4.2.6)
activesupport (= 4.2.5.2) activesupport (= 4.2.6)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.5.2) activerecord (4.2.6)
activemodel (= 4.2.5.2) activemodel (= 4.2.6)
activesupport (= 4.2.5.2) activesupport (= 4.2.6)
arel (~> 6.0) arel (~> 6.0)
activerecord-session_store (0.1.2) activerecord-session_store (0.1.2)
actionpack (>= 4.0.0, < 5) actionpack (>= 4.0.0, < 5)
activerecord (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5)
railties (>= 4.0.0, < 5) railties (>= 4.0.0, < 5)
activesupport (4.2.5.2) activesupport (4.2.6)
i18n (~> 0.7) i18n (~> 0.7)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
@ -45,7 +45,7 @@ GEM
ast (2.2.0) ast (2.2.0)
autoprefixer-rails (6.3.3.1) autoprefixer-rails (6.3.3.1)
execjs execjs
biz (1.3.4) biz (1.4.0)
clavius (~> 1.0) clavius (~> 1.0)
tzinfo tzinfo
browser (2.0.2) browser (2.0.2)
@ -76,7 +76,7 @@ GEM
diffy (3.1.0) diffy (3.1.0)
dnsruby (1.59.2) dnsruby (1.59.2)
docile (1.1.5) docile (1.1.5)
domain_name (0.5.20160216) domain_name (0.5.20160309)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
eco (1.0.0) eco (1.0.0)
coffee-script coffee-script
@ -90,7 +90,7 @@ GEM
dnsruby (>= 1.5) dnsruby (>= 1.5)
equalizer (0.0.10) equalizer (0.0.10)
erubis (2.7.0) erubis (2.7.0)
eventmachine (1.0.9.1) eventmachine (1.2.0.1)
execjs (2.6.0) execjs (2.6.0)
faraday (0.9.2) faraday (0.9.2)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
@ -172,8 +172,8 @@ GEM
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
omniauth-facebook (3.0.0) omniauth-facebook (3.0.0)
omniauth-oauth2 (~> 1.2) omniauth-oauth2 (~> 1.2)
omniauth-google-oauth2 (0.3.1) omniauth-google-oauth2 (0.4.0)
jwt (~> 1.0) jwt (~> 1.5.0)
multi_json (~> 1.3) multi_json (~> 1.3)
omniauth (>= 1.1.1) omniauth (>= 1.1.1)
omniauth-oauth2 (>= 1.3.1) omniauth-oauth2 (>= 1.3.1)
@ -199,22 +199,22 @@ GEM
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
slop (~> 3.4) slop (~> 3.4)
puma (2.16.0) puma (3.1.0)
rack (1.6.4) rack (1.6.4)
rack-livereload (0.3.16) rack-livereload (0.3.16)
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.2.5.2) rails (4.2.6)
actionmailer (= 4.2.5.2) actionmailer (= 4.2.6)
actionpack (= 4.2.5.2) actionpack (= 4.2.6)
actionview (= 4.2.5.2) actionview (= 4.2.6)
activejob (= 4.2.5.2) activejob (= 4.2.6)
activemodel (= 4.2.5.2) activemodel (= 4.2.6)
activerecord (= 4.2.5.2) activerecord (= 4.2.6)
activesupport (= 4.2.5.2) activesupport (= 4.2.6)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.2.5.2) railties (= 4.2.6)
sprockets-rails sprockets-rails
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
@ -226,23 +226,23 @@ GEM
loofah (~> 2.0) loofah (~> 2.0)
rails-observers (0.1.2) rails-observers (0.1.2)
activemodel (~> 4.0) activemodel (~> 4.0)
railties (4.2.5.2) railties (4.2.6)
actionpack (= 4.2.5.2) actionpack (= 4.2.6)
activesupport (= 4.2.5.2) activesupport (= 4.2.6)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.1.0) rainbow (2.1.0)
rake (10.5.0) rake (11.1.1)
rb-fsevent (0.9.7) rb-fsevent (0.9.7)
rb-inotify (0.9.7) rb-inotify (0.9.7)
ffi (>= 0.5.0) ffi (>= 0.5.0)
ref (2.0.0) ref (2.0.0)
rubocop (0.37.2) rubocop (0.38.0)
parser (>= 2.3.0.4, < 3.0) parser (>= 2.3.0.6, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 0.3) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.7.5) ruby-progressbar (1.7.5)
rubyzip (1.2.0) rubyzip (1.2.0)
sass (3.4.21) sass (3.4.21)
@ -253,9 +253,8 @@ GEM
sprockets-rails (>= 2.0, < 4.0) sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3) tilt (>= 1.1, < 3)
scrub_rb (1.0.1) scrub_rb (1.0.1)
selenium-webdriver (2.52.0) selenium-webdriver (2.53.0)
childprocess (~> 0.5) childprocess (~> 0.5)
multi_json (~> 1.0)
rubyzip (~> 1.0) rubyzip (~> 1.0)
websocket (~> 1.0) websocket (~> 1.0)
shellany (0.0.1) shellany (0.0.1)
@ -273,7 +272,7 @@ GEM
sprockets (3.5.2) sprockets (3.5.2)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
rack (> 1, < 3) rack (> 1, < 3)
sprockets-rails (3.0.3) sprockets-rails (3.0.4)
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
@ -305,7 +304,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.2) unf_ext (0.0.7.2)
unicode-display_width (0.3.1) unicode-display_width (1.0.2)
websocket (1.2.2) websocket (1.2.2)
writeexcel (1.0.5) writeexcel (1.0.5)
zendesk_api (1.13.4) zendesk_api (1.13.4)
@ -352,9 +351,9 @@ DEPENDENCIES
omniauth-linkedin omniauth-linkedin
omniauth-twitter omniauth-twitter
pre-commit pre-commit
puma (< 3.0) puma
rack-livereload rack-livereload
rails (= 4.2.5.2) rails (= 4.2.6)
rails-observers rails-observers
rb-fsevent rb-fsevent
rubocop rubocop

View file

@ -728,6 +728,9 @@ class App.WizardModal extends App.Controller
if type if type
@$(".#{screen}").find("[name=\"options::#{field}\"]").closest('.form-group').addClass('has-error') @$(".#{screen}").find("[name=\"options::#{field}\"]").closest('.form-group').addClass('has-error')
render: ->
# do nothing
class App.WizardFullScreen extends App.WizardModal class App.WizardFullScreen extends App.WizardModal
className: 'getstarted fit' className: 'getstarted fit'

View file

@ -17,13 +17,13 @@ class App.UiElement.active extends App.UiElement.ApplicationUiElement
attribute.name = '{boolean}' + attribute.name attribute.name = '{boolean}' + attribute.name
# build options list based on config # build options list based on config
@getConfigOptionList( attribute, params ) @getConfigOptionList(attribute, params)
# sort attribute.options # sort attribute.options
@sortOptions( attribute, params ) @sortOptions(attribute, params)
# finde selected/checked item of list # finde selected/checked item of list
@selectedOptions( attribute, params ) @selectedOptions(attribute, params)
# return item # return item
$( App.view('generic/select')( attribute: attribute ) ) $( App.view('generic/select')( attribute: attribute ) )

View file

@ -14,13 +14,13 @@ class App.UiElement.boolean extends App.UiElement.ApplicationUiElement
attribute.name = '{boolean}' + attribute.name attribute.name = '{boolean}' + attribute.name
# build options list based on config # build options list based on config
@getConfigOptionList( attribute, params ) @getConfigOptionList(attribute, params)
# sort attribute.options # sort attribute.options
@sortOptions( attribute, params ) @sortOptions(attribute, params)
# finde selected/checked item of list # finde selected/checked item of list
@selectedOptions( attribute, params ) @selectedOptions(attribute, params)
# return item # return item
$( App.view('generic/select')( attribute: attribute ) ) $(App.view('generic/select')(attribute: attribute))

View file

@ -3,24 +3,24 @@ class App.UiElement.radio extends App.UiElement.ApplicationUiElement
@render: (attribute, params) -> @render: (attribute, params) ->
# build options list based on config # build options list based on config
@getConfigOptionList( attribute, params ) @getConfigOptionList(attribute, params)
# build options list based on relation # build options list based on relation
@getRelationOptionList( attribute, params ) @getRelationOptionList(attribute, params)
# add null selection if needed # add null selection if needed
@addNullOption( attribute, params ) @addNullOption(attribute, params)
# sort attribute.options # sort attribute.options
@sortOptions( attribute, params ) @sortOptions(attribute, params)
# finde selected/checked item of list # finde selected/checked item of list
@selectedOptions( attribute, params ) @selectedOptions(attribute, params)
# disable item of list # disable item of list
@disabledOptions( attribute, params ) @disabledOptions(attribute, params)
# filter attributes # filter attributes
@filterOption( attribute, params ) @filterOption(attribute, params)
$( App.view('generic/radio')( attribute: attribute ) ) $( App.view('generic/radio')( attribute: attribute ) )

View file

@ -9,25 +9,25 @@ class App.UiElement.select extends App.UiElement.ApplicationUiElement
attribute.multiple = '' attribute.multiple = ''
# build options list based on config # build options list based on config
@getConfigOptionList( attribute, params ) @getConfigOptionList(attribute, params)
# build options list based on relation # build options list based on relation
@getRelationOptionList( attribute, params ) @getRelationOptionList(attribute, params)
# add null selection if needed # add null selection if needed
@addNullOption( attribute, params ) @addNullOption(attribute, params)
# sort attribute.options # sort attribute.options
@sortOptions( attribute, params ) @sortOptions(attribute, params)
# finde selected/checked item of list # finde selected/checked item of list
@selectedOptions( attribute, params ) @selectedOptions(attribute, params)
# disable item of list # disable item of list
@disabledOptions( attribute, params ) @disabledOptions(attribute, params)
# filter attributes # filter attributes
@filterOption( attribute, params ) @filterOption(attribute, params)
# return item # return item
$( App.view('generic/select')( attribute: attribute ) ) $( App.view('generic/select')( attribute: attribute ) )

View file

@ -5,5 +5,5 @@ class App.UiElement.tag
a = -> a = ->
$('#' + attribute.id ).tokenfield() $('#' + attribute.id ).tokenfield()
$('#' + attribute.id ).parent().css('height', 'auto') $('#' + attribute.id ).parent().css('height', 'auto')
App.Delay.set( a, 120, undefined, 'tags' ) App.Delay.set(a, 120, undefined, 'tags')
item item

View file

@ -13,7 +13,7 @@ class App.UiElement.textarea
if visible && !$( item[0] ).expanding('active') if visible && !$( item[0] ).expanding('active')
$( item[0] ).expanding().focus() $( item[0] ).expanding().focus()
) )
App.Delay.set( a, 80 ) App.Delay.set(a, 80)
if attribute.upload if attribute.upload
@ -39,5 +39,5 @@ class App.UiElement.textarea
fail: '' fail: ''
debug: false debug: false
) )
App.Delay.set( u, 100, undefined, 'form_upload' ) App.Delay.set(u, 100, undefined, 'form_upload')
item item

View file

@ -36,7 +36,7 @@ class App.UiElement.ticket_perform_action
item.find('.js-attributeSelector').prepend(selector) item.find('.js-attributeSelector').prepend(selector)
# add filter # add filter
item.find('.js-add').bind('click', (e) -> item.find('.js-add').bind('click', (e) =>
element = $(e.target).closest('.js-filterElement') element = $(e.target).closest('.js-filterElement')
elementClone = element.clone(true) elementClone = element.clone(true)
element.after(elementClone) element.after(elementClone)

View file

@ -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] }"

View file

@ -15,13 +15,13 @@ class App.UiElement.timezone extends App.UiElement.ApplicationUiElement
attribute.options.push item attribute.options.push item
# add null selection if needed # add null selection if needed
@addNullOption( attribute, params ) @addNullOption(attribute, params)
# sort attribute.options # sort attribute.options
@sortOptions( attribute, params ) @sortOptions(attribute, params)
# finde selected/checked item of list # finde selected/checked item of list
@selectedOptions( attribute, params ) @selectedOptions(attribute, params)
attribute.tag = 'searchable_select' attribute.tag = 'searchable_select'
attribute.placeholder = App.i18n.translateInline('Enter timzone...') attribute.placeholder = App.i18n.translateInline('Enter timzone...')

View file

@ -59,7 +59,7 @@ class Index extends App.WizardFullScreen
App.Config.set( 'getting_started', Index, 'Routes' ) App.Config.set( 'getting_started', Index, 'Routes' )
class AutoWizard extends App.ControllerContent class AutoWizard extends App.WizardFullScreen
constructor: -> constructor: ->
super super

View file

@ -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')

View file

@ -77,21 +77,38 @@ class App.TicketZoomArticleActions extends App.Controller
href: '#' href: '#'
} }
recipients = [] recipients = []
if article.sender.name is 'Agent' if article.sender.name is 'Customer'
if article.to
localRecipients = emailAddresses.parseAddressList(article.to)
if localRecipients
recipients = recipients.concat localRecipients
else
if article.from if article.from
localRecipients = emailAddresses.parseAddressList(article.from) localRecipients = emailAddresses.parseAddressList(article.from)
if localRecipients if localRecipients
recipients = recipients.concat localRecipients recipients = recipients.concat localRecipients
if article.to
localRecipients = emailAddresses.parseAddressList(article.to)
if localRecipients
recipients = recipients.concat localRecipients
if article.cc if article.cc
localRecipients = emailAddresses.parseAddressList(article.cc) localRecipients = emailAddresses.parseAddressList(article.cc)
if localRecipients if localRecipients
recipients = recipients.concat 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 { actions.push {
name: 'reply all' name: 'reply all'
type: 'emailReplyAll' type: 'emailReplyAll'

View file

@ -100,7 +100,7 @@ class ArticleViewItem extends App.Controller
# set @el attributes # set @el attributes
if !article if !article
@el.addClass("ticket-article-item #{@article.sender.name.toLowerCase()}") @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}") @el.attr('id', "article-#{@article.id}")
# set internal change directly in dom, without rerender while article # set internal change directly in dom, without rerender while article
@ -166,7 +166,7 @@ class ArticleViewItem extends App.Controller
@shown = false @shown = false
a = => a = =>
@setSeeMore() @setSeeMore()
@delay( a, 50 ) @delay(a, 50)
# set highlighter # set highlighter
@setHighlighter() @setHighlighter()

View file

@ -1,88 +1,30 @@
class Index extends App.ControllerTabs class Index extends App.ControllerContent
header: 'Trigger'
constructor: -> constructor: ->
super super
@title 'Trigger', true # check authentication
return if !@authenticate(false, 'Admin')
@tabs = [ new App.ControllerGenericIndex(
{ el: @el
name: 'Time Based', id: @id
target: 'c-time-based', genericObject: 'Trigger'
controller: App.TriggerTime, defaultSortBy: 'name'
}, #groupBy: 'role'
{
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(
pageData: pageData:
object: 'Jobs' title: 'Triggers'
genericObject: 'Job' 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') 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')

View file

@ -98,10 +98,10 @@ class _webSocketSingleton extends App.Controller
# send ping after visibilitychange to check if connection is open again after wakeup # send ping after visibilitychange to check if connection is open again after wakeup
$(document).bind('visibilitychange', => $(document).bind('visibilitychange', =>
console.log('visibilitychange') @log 'debug', 'visibilitychange'
return if document.hidden return if document.hidden
return if !@connectionEstablished return if !@connectionEstablished
console.log('ping') @log 'debug', 'ping'
@ping() @ping()
) )

View file

@ -1,27 +1,30 @@
class App.Job extends App.Model 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 @extend Spine.Model.Ajax
@url: @apiPath + '/jobs' @url: @apiPath + '/jobs'
@configure_attributes = [ @configure_attributes = [
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false }, { 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: 'timeplan', display: 'When should the job run?', tag: 'timer', null: true },
{ name: 'condition', display: 'Conditions for matching objects.', tag: 'ticket_selector', null: true }, { name: 'condition', display: 'Conditions for effected objects', tag: 'ticket_selector', null: true },
{ name: 'execute', display: 'Execute changes on objects.', tag: 'ticket_perform_action', null: true }, { name: 'perform', 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: 'disable_notiifcation', display: 'Disable Notifications', tag: 'boolean', default: true },
{ name: 'active', display: 'Active', tag: 'active', default: true }, { name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true },
{ name: 'matching', display: 'Matching', readonly: 1 }, { name: 'active', display: 'Active', tag: 'active', default: true },
{ name: 'processed', display: 'Processed', readonly: 1 }, { name: 'matching', display: 'Will process', readonly: 1 },
{ name: 'last_run_at', display: 'Last run', tag: 'datetime', readonly: 1 }, { name: 'processed', display: 'Has processed', readonly: 1 },
{ name: 'running', display: 'Running', tag: 'boolean', readonly: 1 }, { name: 'last_run_at', display: 'Last run', tag: 'datetime', readonly: 1 },
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 }, { name: 'next_run_at', display: 'Scheduled for', tag: 'datetime', readonly: 1 },
{ name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 }, { name: 'running', display: 'Running', tag: 'boolean', readonly: 1 },
{ name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 }, { name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 },
{ name: 'updated_at', display: 'Updated', tag: 'datetime', 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_delete = true
@configure_overview = [ @configure_overview = [
'name', 'name',
'last_run_at', 'last_run_at',
'matching',
'processed', 'processed',
'next_run_at',
'matching',
] ]

View file

@ -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....
'''

View file

@ -0,0 +1,26 @@
<p>
<output class="timer-output js-timerResult"></output>
</p>
<div class="select-boxes">
<div class="select-box select-box--vertical js-day">
<div class="select-box-header"><%- @T('Day') %></div>
<% for day, dayLong of @days: %>
<div data-type="day" class="select-value <% if @attribute.value.days[day]: %>is-selected<% end %>" data-value="<%- day %>"><%- @T(dayLong) %></div>
<input type="hidden" name="{boolean}<%= @attribute.name %>::days::<%- day %>" value="<% if @attribute.value.days[day]: %>true<% else: %>false<% end %>">
<% end %>
</div>
<div class="select-box select-box--four js-hour">
<div data-type="hour" class="select-box-header"><%- @T('Hour') %></div>
<% for hour, hourLong of @hours: %>
<div data-type="hour" class="select-value <% if @attribute.value.hours[hour]: %>is-selected<% end %>" data-value="<%- hour %>"><%- hourLong %></div>
<input type="hidden" name="{boolean}<%= @attribute.name %>::hours::<%- hour %>" value="<% if @attribute.value.hours[hour]: %>true<% else: %>false<% end %>">
<% end %>
</div>
<div class="select-box select-box--vertical js-minute">
<div data-type="minute" class="select-box-header"><%- @T('Minute') %></div>
<% for minute, minuteLong of @minutes: %>
<div data-type="minute" class="select-value <% if @attribute.value.minutes[minute]: %>is-selected<% end %>" data-value="<%- minute %>"><%- minuteLong %></div>
<input type="hidden" name="{boolean}<%= @attribute.name %>::minutes::<%- minute %>" value="<% if @attribute.value.minutes[minute]: %>true<% else: %>false<% end %>">
<% end %>
</div>
</div>

View file

@ -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

View file

@ -92,7 +92,7 @@ returns
=begin =begin
set rellations of model based on params set relations of model based on params
model = Model.find(1) model = Model.find(1)
result = model.param_set_associations(params) result = model.param_set_associations(params)
@ -117,6 +117,7 @@ returns
end end
list = [] list = []
list_of_items.each {|item| list_of_items.each {|item|
next if !item
list.push(assoc.klass.find(item)) list.push(assoc.klass.find(item))
} }
send(assoc.name.to_s + '=', list) send(assoc.name.to_s + '=', list)

View file

@ -507,7 +507,7 @@ retrns
end end
end end
# execute ticket events # execute ticket notification events
Observer::Ticket::Notification.transaction Observer::Ticket::Notification.transaction
# run postmaster post filter # run postmaster post filter

View file

@ -3,85 +3,233 @@
class Job < ApplicationModel class Job < ApplicationModel
store :timeplan store :timeplan
store :condition store :condition
store :execute store :perform
validates :name, presence: true validates :name, presence: true
before_create :updated_matching before_create :updated_matching, :update_next_run_at
before_update :updated_matching before_update :updated_matching, :update_next_run_at
notify_clients_support notify_clients_support
def self.run def self.run
time = Time.zone.now jobs = Job.where(active: true, running: false)
day_map = {
0 => 'sun',
1 => 'mon',
2 => 'tue',
3 => 'wed',
4 => 'thu',
5 => 'fri',
6 => 'sat',
}
jobs = Job.where( active: true )
jobs.each do |job| 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.executable?
next if job.updated_at > Time.zone.now - 1.minute
# check if jobs need to be executed matching = job.matching_count
# ignore if job was running within last 10 min. if job.matching != matching
next if job.last_run_at && job.last_run_at > Time.zone.now - 10.minutes job.matching = matching
job.save
# 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
end 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.last_run_at = Time.zone.now
job.save job.save
end end
true true
end 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 private
def updated_matching def updated_matching
count = Ticket.where( condition.permit! ).count self.matching = matching_count
self.matching = count
end end
def self.match_minutes(minutes) def update_next_run_at
minutes.gsub!(/(\d)\d/, '\\1') self.next_run_at = next_run_at_calculate
minutes.to_s + '0'
end 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 end

View file

@ -468,7 +468,7 @@ condition example
raise "Invalid selector, operator missing #{selector.inspect}" if !selector['operator'] raise "Invalid selector, operator missing #{selector.inspect}" if !selector['operator']
# validate value / allow empty but only if pre_condition exists # 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?) return nil if selector['pre_condition'].nil? || (selector['pre_condition'].respond_to?(:empty?) && selector['pre_condition'].empty?)
end end
@ -679,7 +679,7 @@ result
return if !customer_id return if !customer_id
customer = User.find( customer_id ) customer = User.find(customer_id)
return if organization_id == customer.organization_id return if organization_id == customer.organization_id
self.organization_id = customer.organization_id self.organization_id = customer.organization_id
@ -691,8 +691,8 @@ result
return if !changes['state_id'] return if !changes['state_id']
# check if new state isn't pending* # check if new state isn't pending*
current_state = Ticket::State.lookup( id: state_id ) current_state = Ticket::State.lookup(id: state_id)
current_state_type = Ticket::StateType.lookup( id: current_state.state_type_id ) current_state_type = Ticket::StateType.lookup(id: current_state.state_type_id)
# in case, set pending_time to nil # in case, set pending_time to nil
return if current_state_type.name =~ /^pending/i return if current_state_type.name =~ /^pending/i
@ -706,7 +706,7 @@ result
articles.destroy_all articles.destroy_all
# destroy online notifications # destroy online notifications
OnlineNotification.remove( self.class.to_s, id ) OnlineNotification.remove(self.class.to_s, id)
end end
end end

View file

@ -13,9 +13,9 @@ module Ticket::Number::Date
# read counter # read counter
counter_increment = nil counter_increment = nil
Ticket::Counter.transaction do Ticket::Counter.transaction do
counter = Ticket::Counter.where( generator: 'Date' ).lock(true).first counter = Ticket::Counter.where(generator: 'Date').lock(true).first
if !counter if !counter
counter = Ticket::Counter.new( generator: 'Date', content: '0' ) counter = Ticket::Counter.new(generator: 'Date', content: '0')
end end
# increase counter # increase counter
@ -48,7 +48,7 @@ module Ticket::Number::Date
mult = 1 mult = 1
(1..number.length).each do |i| (1..number.length).each do |i|
digit = number.to_s[i, 1] digit = number.to_s[i, 1]
chksum = chksum + ( mult * digit.to_i ) chksum = chksum + (mult * digit.to_i)
mult += 1 mult += 1
if mult == 3 if mult == 3
mult = 1 mult = 1
@ -65,6 +65,7 @@ module Ticket::Number::Date
end end
def check(string) def check(string)
return if !string || string.empty?
# get config # get config
system_id = Setting.get('system_id') || '' system_id = Setting.get('system_id') || ''
@ -73,10 +74,15 @@ module Ticket::Number::Date
ticket = nil ticket = nil
# probe format # probe format
if string =~ /#{ticket_hook}#{ticket_hook_divider}(#{system_id}\d{2,50})/i string.scan(/#{ticket_hook}#{ticket_hook_divider}(#{system_id}\d{2,48})/i) {
ticket = Ticket.find_by( number: $1 ) ticket = Ticket.find_by(number: $1)
elsif string =~ /#{ticket_hook}\s{0,2}(#{system_id}\d{2,50})/i break if ticket
ticket = Ticket.find_by( number: $1 ) }
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 end
ticket ticket
end end

View file

@ -12,9 +12,9 @@ module Ticket::Number::Increment
min_digs = config[:min_size] || 4 min_digs = config[:min_size] || 4
counter_increment = nil counter_increment = nil
Ticket::Counter.transaction do Ticket::Counter.transaction do
counter = Ticket::Counter.where( generator: 'Increment' ).lock(true).first counter = Ticket::Counter.where(generator: 'Increment').lock(true).first
if !counter if !counter
counter = Ticket::Counter.new( generator: 'Increment', content: '0' ) counter = Ticket::Counter.new(generator: 'Increment', content: '0')
end end
counter_increment = counter.content.to_i counter_increment = counter.content.to_i
@ -31,9 +31,9 @@ module Ticket::Number::Increment
min_digs = min_digs.to_i - 1 min_digs = min_digs.to_i - 1
end end
fillup = Setting.get('system_id').to_s || '1' 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' fillup = fillup + '0'
} }
@ -53,7 +53,7 @@ module Ticket::Number::Increment
mult = 1 mult = 1
(1..number.length).each do |i| (1..number.length).each do |i|
digit = number.to_s[i, 1] digit = number.to_s[i, 1]
chksum = chksum + ( mult * digit.to_i ) chksum = chksum + (mult * digit.to_i)
mult += 1 mult += 1
if mult == 3 if mult == 3
mult = 1 mult = 1
@ -70,6 +70,7 @@ module Ticket::Number::Increment
end end
def check(string) def check(string)
return if !string || string.empty?
# get config # get config
system_id = Setting.get('system_id') || '' system_id = Setting.get('system_id') || ''
@ -78,10 +79,15 @@ module Ticket::Number::Increment
ticket = nil ticket = nil
# probe format # probe format
if string =~ /#{ticket_hook}#{ticket_hook_divider}(#{system_id}\d{2,48})/i string.scan(/#{ticket_hook}#{ticket_hook_divider}(#{system_id}\d{2,48})/i) {
ticket = Ticket.find_by( number: $1 ) ticket = Ticket.find_by(number: $1)
elsif string =~ /#{ticket_hook}\s{0,2}(#{system_id}\d{2,48})/i break if ticket
ticket = Ticket.find_by( number: $1 ) }
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 end
ticket ticket
end end

View file

@ -0,0 +1,22 @@
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
<script src="/assets/tests/qunit-1.21.0.js"></script>
<script src="/assets/tests/form_timer.js"></script>
<style type="text/css">
body {
padding-top: 0px;
}
</style>
<script type="text/javascript">
</script>
<div id="qunit" class="u-dontfold"></div>
<div>
<form class="form-stacked pull-left">
<div id="forms"></div>
<button type="submit" class="btn btn-primary submit">Submit</button>
</form>
</div>

View file

@ -48,7 +48,7 @@ module Zammad
config.api_path = '/api/v1' config.api_path = '/api/v1'
# define cache store # 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 # default preferences by role
config.preferences_default_by_role = { config.preferences_default_by_role = {

View file

@ -54,9 +54,6 @@ Rails.application.configure do
# Use a different logger for distributed setups. # Use a different logger for distributed setups.
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) # 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. # Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com' # config.action_controller.asset_host = 'http://assets.example.com'
@ -83,7 +80,4 @@ Rails.application.configure do
# format log # format log
config.log_formatter = Logger::Formatter.new config.log_formatter = Logger::Formatter.new
# define cache store
config.cache_store = :file_store, 'tmp/cache_file_store_production'
end end

10
config/puma.rb Normal file
View file

@ -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

View file

@ -8,6 +8,7 @@ Zammad::Application.routes.draw do
match '/tests_form_find', to: 'tests#form_find', via: :get match '/tests_form_find', to: 'tests#form_find', via: :get
match '/tests_form_trim', to: 'tests#form_trim', 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_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_validation', to: 'tests#form_validation', via: :get
match '/tests_form_column_select', to: 'tests#form_column_select', 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 match '/tests_form_searchable_select', to: 'tests#form_searchable_select', via: :get

11
config/routes/trigger.rb Normal file
View file

@ -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

View file

@ -188,7 +188,6 @@ class CreateTicket < ActiveRecord::Migration
add_index :ticket_counters, [:generator], unique: true add_index :ticket_counters, [:generator], unique: true
create_table :overviews do |t| create_table :overviews do |t|
t.references :user, null: true
t.references :role, null: false t.references :role, null: false
t.column :name, :string, limit: 250, null: false t.column :name, :string, limit: 250, null: false
t.column :link, :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.column :created_by_id, :integer, null: false
t.timestamps null: false t.timestamps null: false
end end
add_index :overviews, [:user_id]
add_index :overviews, [:name] 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| create_table :overviews_groups, id: false do |t|
t.integer :overview_id t.integer :overview_id
t.integer :group_id t.integer :group_id
@ -214,13 +219,37 @@ class CreateTicket < ActiveRecord::Migration
add_index :overviews_groups, [:group_id] add_index :overviews_groups, [:group_id]
create_table :triggers do |t| create_table :triggers do |t|
t.column :name, :string, limit: 250, null: false t.column :name, :string, limit: 250, null: false
t.column :key, :string, limit: 250, null: false t.column :condition, :string, limit: 2500, null: false
t.column :value, :string, limit: 250, 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 end
add_index :triggers, [:name] add_index :triggers, [:name], unique: true
add_index :triggers, [:key]
add_index :triggers, [:value] 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| create_table :notifications do |t|
t.column :subject, :string, limit: 250, null: false t.column :subject, :string, limit: 250, null: false

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -3367,6 +3367,15 @@ Scheduler.create_if_not_exists(
updated_by_id: 1, updated_by_id: 1,
created_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( Scheduler.create_if_not_exists(
name: 'Cleanup expired sessions', name: 'Cleanup expired sessions',
method: 'SessionHelper.cleanup_expired', method: 'SessionHelper.cleanup_expired',

View file

@ -73,7 +73,7 @@ returns
# set Settings # set Settings
if auto_wizard_hash['Settings'] if auto_wizard_hash['Settings']
auto_wizard_hash['Settings'].each { |setting_data| auto_wizard_hash['Settings'].each { |setting_data|
Setting.set( setting_data['name'], setting_data['value'] ) Setting.set(setting_data['name'], setting_data['value'])
} }
end end

View file

@ -28,11 +28,7 @@ write a cache
if !params[:expires_in] if !params[:expires_in]
params[:expires_in] = 7.days params[:expires_in] = 7.days
end end
begin Rails.cache.write(key.to_s, data, params)
Rails.cache.write(key.to_s, data, params)
rescue => e
Rails.logger.error "NOTICE: #{e.message}"
end
end end
=begin =begin

View file

@ -0,0 +1,214 @@
test("form elements check", function() {
$('#forms').append('<hr><h1>form elements check</h1><form id="form1"></form>')
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('<hr><h1>form elements check</h1><form id="form2"></form>')
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')
});

View file

@ -16,6 +16,6 @@ echo "export IP=$IP"
echo "export BROWSER_PORT=$BROWSER_PORT" echo "export BROWSER_PORT=$BROWSER_PORT"
#rails s puma -d --pid tmp/pids/server.pid --bind 0.0.0.0 --port $APP_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/websocket-server.rb start -d -p $WS_PORT
script/scheduler.rb start script/scheduler.rb start

View file

@ -63,7 +63,7 @@ start() {
echo -n $"Starting ${NAME}/${RAILS_ENV} application server on port: ${APP_PORT}" echo -n $"Starting ${NAME}/${RAILS_ENV} application server on port: ${APP_PORT}"
# $RAILS server -d -p $APP_PORT --pid $APP_PIDFILE &> /dev/null # $RAILS server -d -p $APP_PORT --pid $APP_PIDFILE &> /dev/null
# thin start --threaded -d -p $APP_PORT --pid $APP_PIDFILE # 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 sleep 2
status -p $APP_PIDFILE &> /dev/null && echo_success || echo_failure status -p $APP_PIDFILE &> /dev/null && echo_success || echo_failure
echo echo

View file

@ -38,16 +38,18 @@ rails r "Setting.set('developer_mode', true)"
pumactl --pidfile tmp/pids/puma.pid stop pumactl --pidfile tmp/pids/puma.pid stop
script/websocket-server.rb 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/websocket-server.rb start -d
script/scheduler.rb start 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://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.32:4444/wd/hub'
#export REMOTE_URL='http://192.168.178.45: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 export RAILS_ENV=test
@ -58,10 +60,10 @@ time rake db:create
echo "rake db:migrate" echo "rake db:migrate"
time 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://localhost:4445"]
#rake test:browser["BROWSER_URL=http://10.0.0.3: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://localhost:4445 BROWSER=chrome"]
#rake test:browser["BROWSER_URL=http://192.168.178.28:4445"]
script/scheduler.rb stop script/scheduler.rb stop
script/websocket-server.rb stop script/websocket-server.rb stop

View file

@ -59,6 +59,13 @@ class AAbUnitTest < TestCase
value: '0', value: '0',
) )
location( url: browser_url + '/tests_form_timer' )
sleep 4
match(
css: '.result .failed',
value: '0',
)
location( url: browser_url + '/tests_form_extended' ) location( url: browser_url + '/tests_form_extended' )
sleep 4 sleep 4
match( match(

View file

@ -95,7 +95,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
owner_id: agent2.id, owner_id: agent2.id,
state: Ticket::State.lookup(name: 'new'), state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -107,7 +107,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
state: Ticket::State.lookup(name: 'pending reminder'), state: Ticket::State.lookup(name: 'pending reminder'),
pending_time: '2016-02-07 16:37:00', pending_time: '2016-02-07 16:37:00',
priority: Ticket::Priority.lookup(name: '2 normal'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -117,9 +117,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
customer_id: customer1.id, customer_id: customer1.id,
owner_id: agent2.id, owner_id: agent2.id,
state: Ticket::State.lookup(name: 'pending reminder'), 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'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -129,9 +129,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
customer_id: customer1.id, customer_id: customer1.id,
owner_id: agent1.id, owner_id: agent1.id,
state: Ticket::State.lookup(name: 'new'), 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'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -143,7 +143,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
state: Ticket::State.lookup(name: 'new'), state: Ticket::State.lookup(name: 'new'),
escalation_time: '2016-02-07 16:37:00', escalation_time: '2016-02-07 16:37:00',
priority: Ticket::Priority.lookup(name: '2 normal'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -166,7 +166,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
owner_id: 1, owner_id: 1,
state: Ticket::State.lookup(name: 'new'), state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -178,7 +178,7 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
state: Ticket::State.lookup(name: 'pending reminder'), state: Ticket::State.lookup(name: 'pending reminder'),
pending_time: '2016-02-08 16:37:00', pending_time: '2016-02-08 16:37:00',
priority: Ticket::Priority.lookup(name: '2 normal'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -188,9 +188,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
customer_id: customer1.id, customer_id: customer1.id,
owner_id: 1, owner_id: 1,
state: Ticket::State.lookup(name: 'pending reminder'), 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'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -200,9 +200,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
customer_id: customer1.id, customer_id: customer1.id,
owner_id: 1, owner_id: 1,
state: Ticket::State.lookup(name: 'new'), 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'), 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, updated_by_id: 1,
created_by_id: 1, created_by_id: 1,
) )
@ -212,9 +212,9 @@ class CalendarSubscriptionTest < ActiveSupport::TestCase
customer_id: customer1.id, customer_id: customer1.id,
owner_id: 1, owner_id: 1,
state: Ticket::State.lookup(name: 'new'), 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'), 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, updated_by_id: 1,
created_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.count, 4)
assert_equal(cal.events[0].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title1 - escalation - group_calendar\'')
assert_equal(cal.events[0].description, "T##{ticket1.number}") assert_equal(cal.events[0].description, "T##{ticket5.number}")
assert_equal(cal.events[0].has_alarm?, false) assert_equal(cal.events[0].has_alarm?, false)
assert_equal(cal.events[1].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title1 - new - group_calendar\'')
assert_equal(cal.events[1].description, "T##{ticket5.number}") assert_equal(cal.events[1].description, "T##{ticket1.number}")
assert_equal(cal.events[1].has_alarm?, false) assert_equal(cal.events[1].has_alarm?, false)
assert_equal(cal.events[2].dtstart, Time.zone.today) 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.count, 8)
assert_equal(cal.events[0].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title2 - escalation - group_calendar\'')
assert_equal(cal.events[0].description, "T##{ticket7.number}") assert_equal(cal.events[0].description, "T##{ticket11.number}")
assert_equal(cal.events[0].has_alarm?, false) assert_equal(cal.events[0].has_alarm?, false)
assert_equal(cal.events[1].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title2 - new - group_calendar\'')
assert_equal(cal.events[1].description, "T##{ticket11.number}") assert_equal(cal.events[1].description, "T##{ticket7.number}")
assert_equal(cal.events[1].has_alarm?, false) assert_equal(cal.events[1].has_alarm?, false)
assert_equal(cal.events[2].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title1 - escalation - group_calendar\'')
assert_equal(cal.events[2].description, "T##{ticket1.number}") assert_equal(cal.events[2].description, "T##{ticket5.number}")
assert_equal(cal.events[2].has_alarm?, false) assert_equal(cal.events[2].has_alarm?, false)
assert_equal(cal.events[3].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title1 - new - group_calendar\'')
assert_equal(cal.events[3].description, "T##{ticket5.number}") assert_equal(cal.events[3].description, "T##{ticket1.number}")
assert_equal(cal.events[3].has_alarm?, false) assert_equal(cal.events[3].has_alarm?, false)
assert_equal(cal.events[4].dtstart, Time.zone.today) 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.count, 4)
assert_equal(cal.events[0].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title1 - escalation - group_default\'')
assert_equal(cal.events[0].description, "T##{ticket2.number}") assert_equal(cal.events[0].description, "T##{ticket6.number}")
assert_equal(cal.events[1].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title1 - new - group_default\'')
assert_equal(cal.events[1].description, "T##{ticket6.number}") assert_equal(cal.events[1].description, "T##{ticket2.number}")
assert_equal(cal.events[2].dtstart, Time.zone.today) 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)') 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.count, 8)
assert_equal(cal.events[0].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title2 - escalation - group_default\'')
assert_equal(cal.events[0].description, "T##{ticket8.number}") assert_equal(cal.events[0].description, "T##{ticket12.number}")
assert_equal(cal.events[0].has_alarm?, false) assert_equal(cal.events[0].has_alarm?, false)
assert_equal(cal.events[1].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title2 - new - group_default\'')
assert_equal(cal.events[1].description, "T##{ticket12.number}") assert_equal(cal.events[1].description, "T##{ticket8.number}")
assert_equal(cal.events[1].has_alarm?, false) assert_equal(cal.events[1].has_alarm?, false)
assert_equal(cal.events[2].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title1 - escalation - group_default\'')
assert_equal(cal.events[2].description, "T##{ticket2.number}") assert_equal(cal.events[2].description, "T##{ticket6.number}")
assert_equal(cal.events[1].has_alarm?, false) assert_equal(cal.events[2].has_alarm?, false)
assert_equal(cal.events[3].dtstart, Time.zone.today) 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].summary, 'new ticket: \'some title1 - new - group_default\'')
assert_equal(cal.events[3].description, "T##{ticket6.number}") assert_equal(cal.events[3].description, "T##{ticket2.number}")
assert_equal(cal.events[3].has_alarm?, false) assert_equal(cal.events[3].has_alarm?, false)
assert_equal(cal.events[4].dtstart, Time.zone.today) assert_equal(cal.events[4].dtstart, Time.zone.today)

View file

@ -85,45 +85,45 @@ no reference "
Setting.set('postmaster_follow_up_search_in', %w(body attachment references)) Setting.set('postmaster_follow_up_search_in', %w(body attachment references))
sleep 1 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) assert_equal(ticket.id, ticket_p.id)
sleep 1 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) assert_equal(ticket.id, ticket_p.id)
sleep 1 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) assert_equal(ticket.id, ticket_p.id)
sleep 1 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) assert_equal(ticket.id, ticket_p.id)
sleep 1 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) assert_equal(ticket.id, ticket_p.id)
Setting.set('postmaster_follow_up_search_in', setting_orig) Setting.set('postmaster_follow_up_search_in', setting_orig)
sleep 1 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) assert_equal(ticket.id, ticket_p.id)
sleep 1 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) assert_not_equal(ticket.id, ticket_p.id)
sleep 1 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) assert_not_equal(ticket.id, ticket_p.id)
sleep 1 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) assert_not_equal(ticket.id, ticket_p.id)
sleep 1 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) assert_not_equal(ticket.id, ticket_p.id)
end end
@ -165,7 +165,51 @@ Auto-Submitted: auto-replied
Some Text" 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) ticket = Ticket.find(ticket.id)
assert_equal(ticket.id, ticket_p.id) assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket.state.name) assert_equal('open', ticket.state.name)

725
test/unit/job_test.rb Normal file
View file

@ -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

View file

@ -146,6 +146,86 @@ class TicketSelectorTest < ActiveSupport::TestCase
ticket_count, tickets = Ticket.selectors(condition, 10, customer1) ticket_count, tickets = Ticket.selectors(condition, 10, customer1)
assert_equal(ticket_count, 0) 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 # search matching
condition = { condition = {
'ticket.group_id' => { 'ticket.group_id' => {