Compare commits
286 Commits
develop
...
stable-2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d3ac8f340 | ||
|
|
7b319d5ffb | ||
|
|
accea5dff8 | ||
|
|
49e20d3655 | ||
|
|
f6ab1cf3c9 | ||
|
|
f21b59ff7f | ||
|
|
2f2c3f41b0 | ||
|
|
c1912c7d3c | ||
|
|
98c9e056ea | ||
|
|
c8777a4b1e | ||
|
|
2eb4fd3e00 | ||
|
|
dcc3a96efa | ||
|
|
8eb858ffc8 | ||
|
|
921e571ae2 | ||
|
|
66e7b87ba3 | ||
|
|
c98a4ac48b | ||
|
|
a257451ee0 | ||
|
|
e3cfd1db35 | ||
|
|
a7efa39a8e | ||
|
|
8620664c07 | ||
|
|
62d86ffe12 | ||
|
|
4ab13580c5 | ||
|
|
038ded71f5 | ||
|
|
0206b17cf9 | ||
|
|
7360844c8e | ||
|
|
342d826ab2 | ||
|
|
3171fedfc6 | ||
|
|
ae46d0accb | ||
|
|
c4ce1caae5 | ||
|
|
0e32d9dc37 | ||
|
|
eb5fe7fdff | ||
|
|
61401f762c | ||
|
|
d6e5e1eaee | ||
|
|
d5f7609a26 | ||
|
|
b390456159 | ||
|
|
76630be5ee | ||
|
|
b568f354b7 | ||
|
|
f7e3412540 | ||
|
|
bf36225be2 | ||
|
|
a4339bd8fa | ||
|
|
1910f8da85 | ||
|
|
db8b85f767 | ||
|
|
f1664c3841 | ||
|
|
151c298056 | ||
|
|
38f51ce6b2 | ||
|
|
434f3e963e | ||
|
|
b853dd1597 | ||
|
|
3b5b6f612f | ||
|
|
962043c520 | ||
|
|
8394be87ed | ||
|
|
5b547ceb3c | ||
|
|
789ee08485 | ||
|
|
490937859a | ||
|
|
dfc306a2ac | ||
|
|
dea06015a1 | ||
|
|
2711e3392d | ||
|
|
f5e1fe0c2f | ||
|
|
dbbb4ac7a1 | ||
|
|
8ec2ffdde1 | ||
|
|
b76664e7ae | ||
|
|
704c537749 | ||
|
|
633c99bb72 | ||
|
|
44051d8023 | ||
|
|
50dc143c1b | ||
|
|
6d44275869 | ||
|
|
b1b3bf6d3c | ||
|
|
77081ecd4d | ||
|
|
b823b960d8 | ||
|
|
e08f634125 | ||
|
|
ab5e8d9338 | ||
|
|
9a29ea8f14 | ||
|
|
cf87e00b21 | ||
|
|
6233b0a9ec | ||
|
|
04b1bc638c | ||
|
|
a78938f7c2 | ||
|
|
1d23308454 | ||
|
|
6bc9c00ab8 | ||
|
|
bc131742be | ||
|
|
5a12aa0167 | ||
|
|
b3ab9d0055 | ||
|
|
1bde8f91bc | ||
|
|
47ef1b7e5d | ||
|
|
88c7816f5c | ||
|
|
82339c6c18 | ||
|
|
04a609c3a8 | ||
|
|
60430139aa | ||
|
|
7d1c655ee3 | ||
|
|
e34c1c9b79 | ||
|
|
3c430b35ef | ||
|
|
7ad3c70311 | ||
|
|
c270bc1e67 | ||
|
|
3d19bfdcf7 | ||
|
|
dc4a55cc5c | ||
|
|
6e8886277d | ||
|
|
74e96e92b2 | ||
|
|
287b1f0d01 | ||
|
|
feee46e29b | ||
|
|
991efe36e4 | ||
|
|
a2aacb20fa | ||
|
|
592e978062 | ||
|
|
a4769c8bc7 | ||
|
|
d1378d53f1 | ||
|
|
f0b91d3269 | ||
|
|
48c77dd688 | ||
|
|
d86d904395 | ||
|
|
5efc455f0c | ||
|
|
be3bbeba20 | ||
|
|
31ab1b614c | ||
|
|
4bc5fa4693 | ||
|
|
418a64f401 | ||
|
|
abe46270a6 | ||
|
|
1249a77150 | ||
|
|
cfa6b9ed16 | ||
|
|
aa2303f999 | ||
|
|
d5616684f8 | ||
|
|
0f75ba57bd | ||
|
|
c83675bbba | ||
|
|
17d16d405c | ||
|
|
76795ca302 | ||
|
|
5d145bd09f | ||
|
|
82471552a8 | ||
|
|
dc60574129 | ||
|
|
d33a96dee2 | ||
|
|
0098d4ab82 | ||
|
|
bb03b5eab8 | ||
|
|
17eb0f84ec | ||
|
|
56d0523c1f | ||
|
|
9fa18758bc | ||
|
|
d2f2c55b06 | ||
|
|
f4a409bd6e | ||
|
|
971b724d4c | ||
|
|
3263c6f2ba | ||
|
|
359b026535 | ||
|
|
c63518fd3e | ||
|
|
24ba3cff44 | ||
|
|
f7066e1995 | ||
|
|
6844499309 | ||
|
|
afa3e47eab | ||
|
|
5f3e9298f1 | ||
|
|
e4dd303357 | ||
|
|
2fa06eee3d | ||
|
|
dc0ac03334 | ||
|
|
c5a1d69ec5 | ||
|
|
9891981f92 | ||
|
|
2c721f71e7 | ||
|
|
0b4a4be0b7 | ||
|
|
b13bb2fcaf | ||
|
|
d9ba369fcf | ||
|
|
3adece0db5 | ||
|
|
7101ffc277 | ||
|
|
19a7a439b8 | ||
|
|
e728320241 | ||
|
|
1bc17afc53 | ||
|
|
ee5c58f53a | ||
|
|
9bdf6d7b81 | ||
|
|
5754fdccdd | ||
|
|
4901050d57 | ||
|
|
7980c8cb93 | ||
|
|
7e7088388d | ||
|
|
2f6e52d63d | ||
|
|
bde8babb43 | ||
|
|
d6d68fb363 | ||
|
|
814ffed0f0 | ||
|
|
b013fb8cbc | ||
|
|
fbd8e63592 | ||
|
|
e8ad3965d7 | ||
|
|
e00981ee23 | ||
|
|
4e74c6beda | ||
|
|
2d09e556d7 | ||
|
|
8cd39398d6 | ||
|
|
94c616ffe3 | ||
|
|
57649bb888 | ||
|
|
7e9442ec4a | ||
|
|
521e6265fd | ||
|
|
a42a9689fc | ||
|
|
d9ffb8001d | ||
|
|
ec86d26ae6 | ||
|
|
e00efc821a | ||
|
|
f04be8ca93 | ||
|
|
c7ff6a323a | ||
|
|
34833416bc | ||
|
|
d7a6e28d81 | ||
|
|
c13531d720 | ||
|
|
4a40cced7d | ||
|
|
450de10cc7 | ||
|
|
5e587a455c | ||
|
|
04aef9b258 | ||
|
|
9b72557d13 | ||
|
|
a5e0bc30c1 | ||
|
|
458c166e9f | ||
|
|
3373e5d141 | ||
|
|
8f6b8de617 | ||
|
|
22b21a82e7 | ||
|
|
b4151b299f | ||
|
|
e969fca8c4 | ||
|
|
e59993a5d6 | ||
|
|
3adc9d4cf1 | ||
|
|
c1153250cb | ||
|
|
7718dc28d3 | ||
|
|
1894580ae6 | ||
|
|
11df714bbf | ||
|
|
72adee37a8 | ||
|
|
0e8f460d73 | ||
|
|
4e62d5d0d2 | ||
|
|
004e5ba457 | ||
|
|
f1a8a8da6f | ||
|
|
bfb44d1189 | ||
|
|
d64afd8d97 | ||
|
|
97cfdb9c4c | ||
|
|
bb336f63f1 | ||
|
|
80da08f447 | ||
|
|
a6db2b7c90 | ||
|
|
e1c3f83324 | ||
|
|
8545a44ad1 | ||
|
|
cfbe7510a2 | ||
|
|
1479c32edb | ||
|
|
a9d428bf5a | ||
|
|
3a62eb11da | ||
|
|
2df5f81029 | ||
|
|
b88c23bc92 | ||
|
|
4144fbaf5b | ||
|
|
86ddc93f52 | ||
|
|
8a1b583535 | ||
|
|
7623628b86 | ||
|
|
f753bc203f | ||
|
|
ff9ba10705 | ||
|
|
fdc25bdc26 | ||
|
|
d154a587ad | ||
|
|
a9cc4f037b | ||
|
|
50d6679189 | ||
|
|
b82cf4e1d6 | ||
|
|
fa6eaa6710 | ||
|
|
2ca17ac1bd | ||
|
|
862ecd7d36 | ||
|
|
e38b72593b | ||
|
|
81883cae8f | ||
|
|
63770c9cbf | ||
|
|
aaf5da55a0 | ||
|
|
8455d9ae17 | ||
|
|
839a8e8eb4 | ||
|
|
7aa3f6eaee | ||
|
|
219abc69a3 | ||
|
|
595822c928 | ||
|
|
32112937b1 | ||
|
|
a0889c5b99 | ||
|
|
4bc281bfb0 | ||
|
|
e12ec607d7 | ||
|
|
545f393293 | ||
|
|
4efa152e93 | ||
|
|
0ebda4f32a | ||
|
|
b94f54799f | ||
|
|
99f0b4e2e2 | ||
|
|
53cb906733 | ||
|
|
2c31fe8a84 | ||
|
|
6c5e673a2c | ||
|
|
574b891a33 | ||
|
|
9ef1d3f58b | ||
|
|
8b3f432746 | ||
|
|
14a9df261e | ||
|
|
5620a2847b | ||
|
|
13aaae3835 | ||
|
|
2d4fcd3a4f | ||
|
|
f4f70b0bc2 | ||
|
|
1d279bb88f | ||
|
|
fe4757cbf9 | ||
|
|
1d476f163e | ||
|
|
513701b6d2 | ||
|
|
1a1b661714 | ||
|
|
6081a98279 | ||
|
|
157991c33d | ||
|
|
ef67be9170 | ||
|
|
39f3819303 | ||
|
|
7a676bd563 | ||
|
|
80c54daa03 | ||
|
|
99c88cde62 | ||
|
|
17d113ca20 | ||
|
|
2fbf4324a7 | ||
|
|
b46c390d30 | ||
|
|
3c3651bc52 | ||
|
|
abeeb5e66d | ||
|
|
09d61ce4c8 | ||
|
|
cc1c4495b2 | ||
|
|
7b1ec4d17f | ||
|
|
99b3201ccf | ||
|
|
d4205d9ba6 | ||
|
|
8d57633390 |
@@ -293,6 +293,7 @@ test:integration:es_mysql:
|
||||
- ruby -I test/ test/controllers/search_controller_test.rb
|
||||
- ruby -I test/ test/integration/report_test.rb
|
||||
- ruby -I test/ test/controllers/form_controller_test.rb
|
||||
- ruby -I test/ test/controllers/user_organization_controller_test.rb
|
||||
- rake db:drop
|
||||
|
||||
test:integration:es_postgresql:
|
||||
@@ -310,6 +311,7 @@ test:integration:es_postgresql:
|
||||
- ruby -I test/ test/controllers/search_controller_test.rb
|
||||
- ruby -I test/ test/integration/report_test.rb
|
||||
- ruby -I test/ test/controllers/form_controller_test.rb
|
||||
- ruby -I test/ test/controllers/user_organization_controller_test.rb
|
||||
- rake db:drop
|
||||
|
||||
test:integration:zendesk_mysql:
|
||||
|
||||
@@ -14,6 +14,11 @@ targets:
|
||||
- elasticsearch
|
||||
- nginx|apache2
|
||||
- postgresql|mysql-server|mariadb-server|sqlite
|
||||
debian-9:
|
||||
dependencies:
|
||||
- elasticsearch
|
||||
- nginx|apache2
|
||||
- postgresql|mariadb-server|sqlite
|
||||
ubuntu-16.04:
|
||||
dependencies:
|
||||
- elasticsearch
|
||||
|
||||
@@ -21,6 +21,7 @@ language: ruby
|
||||
rvm:
|
||||
- 2.4.1
|
||||
before_install:
|
||||
- git fetch --unshallow
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get install -y curl git-core patch build-essential bison zlib1g-dev libssl-dev libxml2-dev libxml2-dev sqlite3 libsqlite3-dev autotools-dev libxslt1-dev libyaml-0-2 autoconf automake libreadline6-dev libyaml-dev libtool libgmp-dev libgdbm-dev libncurses5-dev pkg-config libffi-dev libmysqlclient-dev postfix
|
||||
- if [ "${DB}" = "mysql" ]; then mysql -u root -e "CREATE USER 'some_user'@'localhost' IDENTIFIED BY 'some_pass';"; fi
|
||||
@@ -29,6 +30,7 @@ before_install:
|
||||
- if [ "${DB}" = "postgresql" ]; then psql -c 'create database zammad_test;' -U postgres; fi
|
||||
- if [ "${DB}" = "postgresql" ]; then cp config/database.yml.test-postgresql config/database.yml; fi
|
||||
- export RAILS_ENV=test
|
||||
- export ZAMMAD_VERSION=$(git describe --tags | sed -e 's/-[a-z0-9]\{8,\}.*//g')
|
||||
install:
|
||||
- if [ "${DB}" = "mysql" ]; then bundle install --without postgres; fi
|
||||
- if [ "${DB}" = "postgresql" ]; then bundle install; fi
|
||||
|
||||
83
CHANGELOG.md
83
CHANGELOG.md
@@ -1,16 +1,81 @@
|
||||
# Change Log
|
||||
|
||||
## [1.6.0](https://github.com/zammad/zammad/tree/1.6.0) (2017-xx-xx)
|
||||
[Full Changelog](https://github.com/zammad/zammad/compare/1.4.0...1.5.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
|
||||
|
||||
## [2.0.1](https://github.com/zammad/zammad/tree/2.0.1) (2017-10-25)
|
||||
[Full Changelog](https://github.com/zammad/zammad/compare/2.0.0...2.0.1)
|
||||
|
||||
**Fixed bugs:**
|
||||
- Unable to reindex tickets / elasticsearch error "MapperParsingException\[failed to parse \[article.ticket\]\]; nested: JsonParseException\[Current token \(START\_OBJECT\) not of boolean type" [\#1538](https://github.com/zammad/zammad/issues/1538) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Cannot update notification preferences when nothing is selected [\#1276](https://github.com/zammad/zammad/issues/1276) [[bug](https://github.com/zammad/zammad/labels/bug)] [[notification](https://github.com/zammad/zammad/labels/notification)]
|
||||
- ReplyAll - moves recipients from "To" to "CC" [\#1303](https://github.com/zammad/zammad/issues/1303) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Zammad sends reply to itself [\#986](https://github.com/zammad/zammad/issues/986) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Exchange Feature - URL? [\#1406](https://github.com/zammad/zammad/issues/1406) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Tickets sent by agent via email into Zammad have state open \(not new\) [\#1482](https://github.com/zammad/zammad/issues/1482) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Icinga/Nagios Integration: No longer accepts regex in "Sender" [\#1439](https://github.com/zammad/zammad/issues/1439) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Job BackgroundJobSearchIndex NoMethodError: undefined method `lookup' for User::Permission:Module [\#1424](https://github.com/zammad/zammad/issues/1424) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Profile not clickable on browsers with touch events or on some Chrome for Windows [\#1398](https://github.com/zammad/zammad/issues/1398) [[bug](https://github.com/zammad/zammad/labels/bug)
|
||||
|
||||
# Change Log
|
||||
|
||||
## [2.0.0](https://github.com/zammad/zammad/tree/2.0.0) (2017-09-11)
|
||||
[Full Changelog](https://github.com/zammad/zammad/compare/1.6.0...2.0.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
- Calendar: ICAL ignores recurring appointments/events [\#1362](https://github.com/zammad/zammad/issues/1362) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Inconsistent data state causes PostgreSQL Database to break update/migration with PG::InFailedSqlTransaction [\#1261](https://github.com/zammad/zammad/issues/1261) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Added rtl/i18n support for Hebrew
|
||||
- Chat widget error "chat notice || Translation 'es' needed!" [\#1267](https://github.com/zammad/zammad/issues/1267) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Chat Widget should support richtext \(same as agent chat\) [\#1255](https://github.com/zammad/zammad/issues/1255) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Using text modules also in chat [\#443](https://github.com/zammad/zammad/issues/443) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Added possibility to keep emails on IMAP server (do not delete them from server) [\#539](https://github.com/zammad/zammad/issues/539) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- OAuth2: Office365 [\#1177](https://github.com/zammad/zammad/issues/1177) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Ticket creation - allow to create users without email [\#562](https://github.com/zammad/zammad/issues/562) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Switch elasticsearch standard operator from OR to AND [\#1090](https://github.com/zammad/zammad/issues/1090) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Delete tickets via admin interface as job [\#214](https://github.com/zammad/zammad/issues/214) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Suggestion: More Efficient Use of Ticket Area [\#957](https://github.com/zammad/zammad/issues/957) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
|
||||
**Fixed bugs:**
|
||||
- Macro/Bulk action: Cannot transfer more than one ticket to agent [\#1107](https://github.com/zammad/zammad/issues/1107) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Duplicates in Caller Log \(Sipgate\) [\#1253](https://github.com/zammad/zammad/issues/1253) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Time Accounting not showing all tickets, time is missing completly of specific ticket [\#1315](https://github.com/zammad/zammad/issues/1315) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Exchange config with error message [\#1381](https://github.com/zammad/zammad/issues/1381) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- 'Access-Control-Allow-Origin' error when submitting web form. [\#1318](https://github.com/zammad/zammad/issues/1318) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Delete action for tickets not working [\#1331](https://github.com/zammad/zammad/issues/1331) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Relative and absolute time bug [\#1340](https://github.com/zammad/zammad/issues/1340) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Ticket Hook Position between RE: and the ticket subject [\#1369](https://github.com/zammad/zammad/issues/1369) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Missing replacement of Config and Current User objects in text modules and signatures [\#1290](https://github.com/zammad/zammad/issues/1290) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Tel Protocol Link for Telephone and Mobile [\#991](https://github.com/zammad/zammad/issues/991) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Don't use Proxy on localhost Address. [\#1292](https://github.com/zammad/zammad/issues/1292) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Reopen ticket on mail delivery failure [\#1198](https://github.com/zammad/zammad/issues/1198) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Block escape key when creating / editing text modules [\#1291](https://github.com/zammad/zammad/issues/1291) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||
- Race condition if agents merge ticket at same time but in different directions. [\#1216](https://github.com/zammad/zammad/issues/1216) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Config option to have uniq email addresses for users [\#1251](https://github.com/zammad/zammad/issues/1251) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Feature Request: clear caller log [\#1254](https://github.com/zammad/zammad/issues/1254) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Problem with cyrillic letters in name of overviews. [\#1151](https://github.com/zammad/zammad/issues/1151) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Backslash issue [\#1230](https://github.com/zammad/zammad/issues/1230) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Unable to add Linked Gitlab account [\#1240](https://github.com/zammad/zammad/issues/1240) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Firefox - Focus lost after using :: TextSnippts [\#884](https://github.com/zammad/zammad/issues/884) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||
- Trigger: Wrong behaviour for condition "is not" "not set" [\#1149](https://github.com/zammad/zammad/issues/1149) [[bug](https://github.com/zammad/zammad/labels/bug)] [[trigger](https://github.com/zammad/zammad/labels/trigger)]
|
||||
- Add date/time information to time accounting \(xls export\) [\#1194](https://github.com/zammad/zammad/issues/1194) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Zammad Chat widget not working with jquery 3 [\#1220](https://github.com/zammad/zammad/issues/1220) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Feedback Form Channel - Ability to set a target group for incoming tickets [\#1206](https://github.com/zammad/zammad/issues/1206) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Add recaptcha option to web form channel [\#1207](https://github.com/zammad/zammad/issues/1207) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Too many emails about new client access if user agent is "" [\#1208](https://github.com/zammad/zammad/issues/1208) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- User can not be removed from organization [\#1165](https://github.com/zammad/zammad/issues/1165) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Edge looses text formatting on reply \(may also on other browsers\) [\#707](https://github.com/zammad/zammad/issues/707) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||
- Generation of weekly report fails with internal server error [\#1176](https://github.com/zammad/zammad/issues/1176) [[bug](https://github.com/zammad/zammad/labels/bug)] [[reporting](https://github.com/zammad/zammad/labels/reporting)]
|
||||
- Update of object attribute leads to CSRF token verification failure [\#1034](https://github.com/zammad/zammad/issues/1034) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Not showing TO: / CC: fields on IE11 when answering a ticket [\#764](https://github.com/zammad/zammad/issues/764) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Internet Explorer 11 and lower - no paste from Clipboard possible [\#990](https://github.com/zammad/zammad/issues/990) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Tag Bug - tag is deleted if renamed without changes via admin interface [\#1086](https://github.com/zammad/zammad/issues/1086) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Unprocessable Email \(without email address in from header\) [\#811](https://github.com/zammad/zammad/issues/811) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Bulk Action -\> JS Error - Bug [\#1053](https://github.com/zammad/zammad/issues/1053) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Reply-To is not considered for automatic responses \(triggers\). [\#1006](https://github.com/zammad/zammad/issues/1006) [[bug](https://github.com/zammad/zammad/labels/bug)] [[notification](https://github.com/zammad/zammad/labels/notification)]
|
||||
- Creation of overview fails [\#1014](https://github.com/zammad/zammad/issues/1014) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Gui stuck / Error on random switching through overviews [\#1012](https://github.com/zammad/zammad/issues/1012) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- HTML Table in email being re-formatted [\#930](https://github.com/zammad/zammad/issues/930) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Wrong article order after merging of tickets [\#187](https://github.com/zammad/zammad/issues/187) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||
- Ticket were shown twice [\#655](https://github.com/zammad/zammad/issues/655) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
@@ -217,7 +217,7 @@ GEM
|
||||
mime-types (3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mini_portile2 (2.2.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.10.3)
|
||||
multi_json (1.12.1)
|
||||
multi_xml (0.6.0)
|
||||
@@ -228,8 +228,8 @@ GEM
|
||||
nestful (1.1.1)
|
||||
net-ldap (0.15.0)
|
||||
netrc (0.11.0)
|
||||
nokogiri (1.8.0)
|
||||
mini_portile2 (~> 2.2.0)
|
||||
nokogiri (1.8.1)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nori (2.6.0)
|
||||
notiffany (0.1.1)
|
||||
nenv (~> 0.1)
|
||||
|
||||
@@ -134,18 +134,14 @@ class Index extends App.ControllerSubContent
|
||||
online: true
|
||||
}
|
||||
|
||||
# validation
|
||||
if !params['notification_config']['matrix']
|
||||
@log('error', 'invalid notification_config, matrix is missing')
|
||||
return
|
||||
|
||||
# check missing channels
|
||||
for key, value of params['notification_config']['matrix']
|
||||
if !value.channel
|
||||
value.channel = {
|
||||
email: false
|
||||
online: true
|
||||
}
|
||||
if params['notification_config']
|
||||
for key, value of params['notification_config']['matrix']
|
||||
if !value.channel
|
||||
value.channel = {
|
||||
email: false
|
||||
online: true
|
||||
}
|
||||
|
||||
if !params.notification_config.group_ids || _.isEmpty(params.notification_config.group_ids)
|
||||
params.notification_config.group_ids = ['-']
|
||||
|
||||
@@ -14,7 +14,7 @@ class App.UiElement.autocompletion
|
||||
|
||||
# call calback on init
|
||||
if @callback && attribute.value && @params
|
||||
@callback( @params )
|
||||
@callback(@params)
|
||||
|
||||
b = (event, item) =>
|
||||
# set html form attribute
|
||||
@@ -24,7 +24,7 @@ class App.UiElement.autocompletion
|
||||
# call calback
|
||||
if @callback
|
||||
params = App.ControllerForm.params(form)
|
||||
@callback( params )
|
||||
@callback(params)
|
||||
###
|
||||
$(@local_attribute_full).tagsInput(
|
||||
autocomplete_url: '/users/search',
|
||||
@@ -33,7 +33,7 @@ class App.UiElement.autocompletion
|
||||
auto: {
|
||||
source: '/users/search',
|
||||
minLength: 2,
|
||||
select: ( event, ui ) ->
|
||||
select: (event, ui) ->
|
||||
#@log 'notice', 'selected', event, ui
|
||||
b(event, ui.item)
|
||||
}
|
||||
@@ -45,8 +45,8 @@ class App.UiElement.autocompletion
|
||||
$(local_attribute_full).autocomplete(
|
||||
source: source,
|
||||
minLength: attribute.minLengt || 3,
|
||||
select: ( event, ui ) ->
|
||||
select: (event, ui) ->
|
||||
b(event, ui.item)
|
||||
)
|
||||
App.Delay.set( a, 280, undefined, 'form_autocompletion' )
|
||||
App.Delay.set(a, 280, undefined, 'form_autocompletion')
|
||||
item
|
||||
|
||||
@@ -80,6 +80,17 @@ class App.UiElement.ticket_selector
|
||||
if config.name && config.name.match(myRegExp)
|
||||
config.operator = operator
|
||||
elements["#{groupKey}.#{config.name}"] = config
|
||||
|
||||
if attribute.out_of_office
|
||||
elements['ticket.out_of_office_replacement_id'] =
|
||||
name: 'out_of_office_replacement_id'
|
||||
display: 'Out of office replacement'
|
||||
tag: 'autocompletion_ajax'
|
||||
relation: 'User'
|
||||
null: false
|
||||
translate: true
|
||||
operator: ['is', 'is not']
|
||||
|
||||
[defaults, groups, elements]
|
||||
|
||||
@rowContainer: (groups, elements, attribute) ->
|
||||
|
||||
@@ -67,9 +67,14 @@ class App.UiElement.user_permission
|
||||
item = $( App.view('generic/user_permission')(
|
||||
attribute: attribute
|
||||
roles: roles
|
||||
groups: groups
|
||||
params: params
|
||||
rolesSelected: rolesSelected
|
||||
) )
|
||||
|
||||
item.find('.js-groups').html(App.view('generic/user_permission_group')(
|
||||
attribute: attribute
|
||||
groups: groups
|
||||
params: params
|
||||
groupsSelected: groupsSelected
|
||||
hideGroups: hideGroups
|
||||
groupAccesses: App.Group.accesses()
|
||||
@@ -101,7 +106,11 @@ class App.UiElement.user_permission
|
||||
|
||||
# if role with groups plugin is deselected, hide group selection
|
||||
if !checked
|
||||
if rolesWithGroupPlugin[role_id] is 'group'
|
||||
show = false
|
||||
for role_id, group of rolesWithGroupPlugin
|
||||
if item.find("[name=role_ids][value=#{role_id}]").prop('checked')
|
||||
show = true
|
||||
if !show
|
||||
item.find('.js-groupList').addClass('hidden')
|
||||
|
||||
# select groups if only one is available
|
||||
|
||||
@@ -297,95 +297,17 @@ class App.TicketZoomArticleActions extends App.Controller
|
||||
e.preventDefault()
|
||||
|
||||
# get reference article
|
||||
article_id = $(e.target).parents('[data-id]').data('id')
|
||||
article = App.TicketArticle.fullLocal(article_id)
|
||||
ticket = App.Ticket.fullLocal(article.ticket_id)
|
||||
type = App.TicketArticleType.find(article.type_id)
|
||||
customer = App.User.find(article.created_by_id)
|
||||
article_id = $(e.target).parents('[data-id]').data('id')
|
||||
article = App.TicketArticle.fullLocal(article_id)
|
||||
ticket = App.Ticket.fullLocal(article.ticket_id)
|
||||
type = App.TicketArticleType.find(article.type_id)
|
||||
article_created_by = App.User.find(article.created_by_id)
|
||||
email_addresses = App.EmailAddress.all()
|
||||
|
||||
@scrollToCompose()
|
||||
|
||||
# empty form
|
||||
articleNew = {
|
||||
to: ''
|
||||
cc: ''
|
||||
body: ''
|
||||
in_reply_to: ''
|
||||
}
|
||||
|
||||
#@el.closest('[name="in_reply_to"]').val('')
|
||||
|
||||
if article.message_id
|
||||
articleNew.in_reply_to = article.message_id
|
||||
|
||||
if type.name is 'email' || type.name is 'phone' || type.name is 'web'
|
||||
|
||||
if type.name is 'phone'
|
||||
|
||||
# inbound call
|
||||
if article.sender.name is 'Agent'
|
||||
articleNew.to = article.to
|
||||
|
||||
# outbound call
|
||||
else
|
||||
articleNew.to = article.to
|
||||
|
||||
# if sender is customer but in article.from is no email, try to get
|
||||
# customers email via customer user
|
||||
if articleNew.to && !articleNew.to.match(/@/)
|
||||
articleNew.to = ticket.customer.email
|
||||
|
||||
else
|
||||
user = App.User.find(article.created_by_id)
|
||||
if article.sender.name is 'Agent' && !article.from.match(user.email)
|
||||
articleNew.to = article.to
|
||||
else
|
||||
if article.reply_to
|
||||
articleNew.to = article.reply_to
|
||||
else
|
||||
articleNew.to = article.from
|
||||
|
||||
# if sender is customer but in article.from is no email, try to get
|
||||
# customers email via customer user
|
||||
if articleNew.to && !articleNew.to.match(/@/)
|
||||
articleNew.to = article.created_by.email
|
||||
|
||||
# filter for uniq recipients
|
||||
recipientAddresses = {}
|
||||
recipient = emailAddresses.parseAddressList(articleNew.to)
|
||||
if recipient && recipient[0] && !_.isEmpty(recipient[0].address)
|
||||
recipientAddresses[ recipient[0].address.toString().toLowerCase() ] = true
|
||||
if all
|
||||
addAddresses = (lineNew, addressLine) ->
|
||||
localAddresses = App.EmailAddress.all()
|
||||
recipients = emailAddresses.parseAddressList(addressLine)
|
||||
if recipients
|
||||
for recipient in recipients
|
||||
if !_.isEmpty(recipient.address)
|
||||
|
||||
# check if address is not local
|
||||
localAddress = false
|
||||
for address in localAddresses
|
||||
if !_.isEmpty(recipient.address) && recipient.address.toString().toLowerCase() == address.email.toString().toLowerCase()
|
||||
localAddress = true
|
||||
if !localAddress
|
||||
|
||||
# filter for uniq recipients
|
||||
if !recipientAddresses[ recipient.address.toString().toLowerCase() ]
|
||||
recipientAddresses[ recipient.address.toString().toLowerCase() ] = true
|
||||
|
||||
# add recipient
|
||||
if lineNew
|
||||
lineNew = lineNew + ', '
|
||||
lineNew = lineNew + recipient.address
|
||||
lineNew
|
||||
|
||||
if article.from
|
||||
articleNew.cc = addAddresses(articleNew.cc, article.from)
|
||||
if article.to
|
||||
articleNew.cc = addAddresses(articleNew.cc, article.to)
|
||||
if article.cc
|
||||
articleNew.cc = addAddresses(articleNew.cc, article.cc)
|
||||
articleNew = App.Utils.getRecipientArticle(ticket, article, article_created_by, type, email_addresses, all)
|
||||
|
||||
# get current body
|
||||
body = @el.closest('.ticketZoom').find('.article-add [data-name="body"]').html() || ''
|
||||
|
||||
@@ -27,6 +27,8 @@ class App.TicketZoomSidebar extends App.ObserverController
|
||||
taskGet: @taskGet
|
||||
formMeta: @formMeta
|
||||
markForm: @markForm
|
||||
tags: @tags
|
||||
links: @links
|
||||
)
|
||||
else
|
||||
@sidebarBackends[key].reload(
|
||||
@@ -34,6 +36,8 @@ class App.TicketZoomSidebar extends App.ObserverController
|
||||
query: @query
|
||||
formMeta: @formMeta
|
||||
markForm: @markForm
|
||||
tags: @tags
|
||||
links: @links
|
||||
)
|
||||
item = @sidebarBackends[key].sidebarItem()
|
||||
if item
|
||||
|
||||
@@ -140,7 +140,7 @@ class Index extends App.ControllerSubContent
|
||||
App.Ajax.request(
|
||||
id: 'search'
|
||||
type: 'GET'
|
||||
url: @apiPath + '/users/search'
|
||||
url: "#{@apiPath}/users/search"
|
||||
data:
|
||||
query: @query
|
||||
limit: 140
|
||||
@@ -165,7 +165,7 @@ class Index extends App.ControllerSubContent
|
||||
type: 'GET'
|
||||
url: "#{@apiPath}/users/recent"
|
||||
data:
|
||||
limit: 40
|
||||
limit: 50
|
||||
role_ids: role_ids
|
||||
full: 1
|
||||
processData: true
|
||||
|
||||
@@ -113,6 +113,10 @@ class App.ObjectOrganizationAutocompletion extends App.Controller
|
||||
@createToken name, objectId
|
||||
else
|
||||
if object.email
|
||||
|
||||
# quote name for special character
|
||||
if name.match(/\@|,|;|\^|\+|#|§|\$|%|&|\/|\(|\)|=|\?|!|\*|\[|\]/)
|
||||
name = "\"#{name}\""
|
||||
name += " <#{object.email}>"
|
||||
|
||||
@objectSelect.val(name)
|
||||
|
||||
@@ -905,3 +905,118 @@ class App.Utils
|
||||
placeholder = Array(url_max_length + 1).join('X')
|
||||
text = text.replace(/http(s|):\/\/[-A-Za-z0-9+&@#\/%?=~_\|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]/img, placeholder)
|
||||
text.length
|
||||
|
||||
@getRecipientArticle: (ticket, article, article_created_by, type, email_addresses = [], all) ->
|
||||
|
||||
# empty form
|
||||
articleNew = {
|
||||
to: ''
|
||||
cc: ''
|
||||
body: ''
|
||||
in_reply_to: ''
|
||||
}
|
||||
|
||||
if article.message_id
|
||||
articleNew.in_reply_to = article.message_id
|
||||
|
||||
if type.name is 'phone'
|
||||
|
||||
# inbound call
|
||||
if article.sender.name is 'Agent'
|
||||
if article.to
|
||||
articleNew.to = article.to
|
||||
|
||||
# outbound call
|
||||
else if article.to
|
||||
articleNew.to = article.to
|
||||
|
||||
# if sender is customer but in article.from is no email, try to get
|
||||
# customers email via customer user
|
||||
if articleNew.to && !articleNew.to.match(/@/)
|
||||
articleNew.to = ticket.customer.email
|
||||
|
||||
return articleNew
|
||||
|
||||
if type.name is 'email' || type.name is 'web'
|
||||
localEmailAddresses = []
|
||||
for address in email_addresses
|
||||
if address && !_.isEmpty(address.email)
|
||||
localEmailAddresses.push address.email.toString().toLowerCase()
|
||||
|
||||
isLocalAddress = (address) ->
|
||||
return false if _.isEmpty(address)
|
||||
_.contains(localEmailAddresses, address.toString().toLowerCase())
|
||||
|
||||
article_created_by_email = undefined
|
||||
if article_created_by && article_created_by.email
|
||||
article_created_by_email = article_created_by.email.toLowerCase()
|
||||
|
||||
# check if article sender is local
|
||||
if !_.isEmpty(article.from)
|
||||
senderIsLocal = false
|
||||
senders = emailAddresses.parseAddressList(article.from)
|
||||
if senders && senders[0] && senders[0].address
|
||||
senderIsLocal = isLocalAddress(senders[0].address)
|
||||
|
||||
# sender is local
|
||||
if senderIsLocal
|
||||
articleNew.to = article.to
|
||||
|
||||
# sender is agent from is different (sender was system)
|
||||
else if article.sender.name is 'Agent' && article_created_by_email && article.from && !article.from.match(article_created_by_email)
|
||||
articleNew.to = article.to
|
||||
|
||||
# sender was regular customer
|
||||
else
|
||||
if article.reply_to
|
||||
articleNew.to = article.reply_to
|
||||
else
|
||||
articleNew.to = article.from
|
||||
|
||||
# if sender is customer but in article.from is no email, try to get
|
||||
# customers email via customer user
|
||||
if articleNew.to && !articleNew.to.match(/@/)
|
||||
articleNew.to = article.created_by.email
|
||||
|
||||
# filter for uniq recipients
|
||||
recipientAddresses = {}
|
||||
|
||||
addAddresses = (addressLine, line) ->
|
||||
lineNew = ''
|
||||
recipients = emailAddresses.parseAddressList(addressLine)
|
||||
if !_.isEmpty(recipients)
|
||||
for recipient in recipients
|
||||
if !_.isEmpty(recipient.address)
|
||||
localRecipientAddress = recipient.address.toString().toLowerCase()
|
||||
|
||||
# check if address is not local
|
||||
if !isLocalAddress(localRecipientAddress)
|
||||
|
||||
# filter for uniq recipients
|
||||
if !recipientAddresses[localRecipientAddress]
|
||||
recipientAddresses[localRecipientAddress] = true
|
||||
|
||||
# add recipient
|
||||
if lineNew
|
||||
lineNew = lineNew + ', '
|
||||
lineNew = lineNew + localRecipientAddress
|
||||
|
||||
lineNew
|
||||
if !_.isEmpty(line)
|
||||
if !_.isEmpty(lineNew)
|
||||
lineNew += ', '
|
||||
lineNew += line
|
||||
lineNew
|
||||
|
||||
if articleNew.to
|
||||
articleNew.to = addAddresses(articleNew.to)
|
||||
|
||||
if all
|
||||
if article.from
|
||||
articleNew.to = addAddresses(article.from, articleNew.to)
|
||||
if article.to
|
||||
articleNew.to = addAddresses(article.to, articleNew.to)
|
||||
if article.cc
|
||||
articleNew.cc = addAddresses(article.cc, articleNew.cc)
|
||||
|
||||
articleNew
|
||||
|
||||
@@ -945,9 +945,9 @@ function handleOpts(opts, defs) {
|
||||
parse5322.parseOneAddress = parseOneAddressSimple;
|
||||
parse5322.parseAddressList = parseAddressListSimple;
|
||||
|
||||
// if electron is used, go back to non CommonJS
|
||||
//if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined' && !(window.process && window.process.version && window.process.versions.electron)) {
|
||||
// in Zammad context, go back to non CommonJS
|
||||
// if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||
if (false) {
|
||||
module.exports = parse5322;
|
||||
} else {
|
||||
global.emailAddresses = parse5322;
|
||||
|
||||
@@ -40,6 +40,5 @@ class App.Group extends App.Model
|
||||
read: 'Read'
|
||||
create: 'Create'
|
||||
change: 'Change'
|
||||
delete: 'Delete'
|
||||
overview: 'Overview'
|
||||
full: 'Full'
|
||||
|
||||
@@ -9,7 +9,7 @@ class App.Overview extends App.Model
|
||||
{ name: 'user_ids', display: 'Available for User', tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'firstname' },
|
||||
{ name: 'organization_shared', display: 'Only available for Users with shared Organization', tag: 'select', options: { true: 'yes', false: 'no' }, default: false, null: true },
|
||||
{ name: 'out_of_office', display: 'Only available for Users which are replacements for other users.', tag: 'select', options: { true: 'yes', false: 'no' }, default: false, null: true },
|
||||
{ name: 'condition', display: 'Conditions for shown Tickets', tag: 'ticket_selector', null: false },
|
||||
{ name: 'condition', display: 'Conditions for shown Tickets', tag: 'ticket_selector', null: false, out_of_office: true },
|
||||
{ name: 'prio', display: 'Prio', readonly: 1 },
|
||||
{
|
||||
name: 'view::s'
|
||||
|
||||
@@ -5,7 +5,7 @@ class App.Trigger extends App.Model
|
||||
@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, preview: false, action: true, hasChanged: true },
|
||||
{ name: 'perform', display: 'Execute changes on objects', tag: 'ticket_perform_action', null: true, notification: true },
|
||||
{ name: 'perform', display: 'Execute changes on objects', tag: 'ticket_perform_action', null: true, notification: true, trigger: true },
|
||||
{ name: 'active', display: 'Active', tag: 'active', default: true },
|
||||
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
|
||||
]
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
<div class="checkbox <%= @attribute.class %> checkbox">
|
||||
<% showGroups = false %>
|
||||
<% for role in @roles: %>
|
||||
<% if role.permissions: %>
|
||||
<% for permission in role.permissions: %>
|
||||
<% if _.contains(permission.preferences.plugin, 'groups'): %>
|
||||
<% if showGroups is true: %>
|
||||
<% showGroups = false %>
|
||||
<% break %>
|
||||
<% end %>
|
||||
<% showGroups = true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% for role in @roles: %>
|
||||
<label class="inline-label checkbox-replacement">
|
||||
<input type="checkbox" value="<%= role.id %>" name="role_ids" <% if @rolesSelected[role.id]: %>checked<% end %>/>
|
||||
@@ -20,40 +6,6 @@
|
||||
<%- @Icon('checkbox-checked', 'icon-checked') %>
|
||||
<span class="label-text"><%- @T(role.displayName()) %> <% if role.note: %>- <span class="help-text"><%- @T(role.note) %></span><% end %></span>
|
||||
</label>
|
||||
<% if role.permissions: %>
|
||||
<% for permission in role.permissions: %>
|
||||
<% if showGroups is true && _.contains(permission.preferences.plugin, 'groups'): %>
|
||||
<div style="padding-left: 18px; padding-top: 10px;" class="js-groupList <% if @hideGroups: %>js-groupListHide hidden<% end %>">
|
||||
<table class="settings-list">
|
||||
<thead>
|
||||
<th><%- @T('Group') %>
|
||||
<% for key, text of @groupAccesses: %>
|
||||
<th><%- @T(text) %>
|
||||
<% end %>
|
||||
<tbody>
|
||||
<% for group in @groups: %>
|
||||
<% permissions = [] %>
|
||||
<% if @params.group_ids && @params.group_ids[group.id]: %>
|
||||
<% permissions = @params.group_ids[group.id] %>
|
||||
<% end %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= group.displayName() %>
|
||||
<% for key, text of @groupAccesses: %>
|
||||
<td>
|
||||
<label class="inline-label checkbox-replacement">
|
||||
<input class="js-groupListItem" type="checkbox" value="<%= key %>" name="group_ids::<%= group.id %>" <% if _.contains(permissions, key): %>checked<% end %>/>
|
||||
<%- @Icon('checkbox', 'icon-unchecked') %>
|
||||
<%- @Icon('checkbox-checked', 'icon-checked') %>
|
||||
</label>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</div>
|
||||
<% break %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="js-groups"></div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<div style="padding-left: 18px; padding-top: 10px; width: 95%;" class="js-groupList <% if @hideGroups: %>js-groupListHide hidden<% end %>">
|
||||
<hr>
|
||||
<table class="settings-list" style="width: 100%;">
|
||||
<thead>
|
||||
<th><%- @T('Group') %>
|
||||
<% for key, text of @groupAccesses: %>
|
||||
<th><%- @T(text) %>
|
||||
<% end %>
|
||||
<tbody>
|
||||
<% for group in @groups: %>
|
||||
<% permissions = [] %>
|
||||
<% if @params.group_ids && @params.group_ids[group.id]: %>
|
||||
<% permissions = @params.group_ids[group.id] %>
|
||||
<% end %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= group.displayName() %>
|
||||
<% for key, text of @groupAccesses: %>
|
||||
<td>
|
||||
<label class="inline-label checkbox-replacement">
|
||||
<input class="js-groupListItem" type="checkbox" value="<%= key %>" name="group_ids::<%= group.id %>" <% if _.contains(permissions, key): %>checked<% end %>/>
|
||||
<%- @Icon('checkbox', 'icon-unchecked') %>
|
||||
<%- @Icon('checkbox-checked', 'icon-checked') %>
|
||||
</label>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</div>
|
||||
@@ -6103,6 +6103,11 @@ footer {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.dropdown-backdrop {
|
||||
position: fixed;
|
||||
@extend .zIndex-5; // has to be behind dropdown-menu (dropdown-menu is also zIndex-5 but behind dropdown-backdrop in dom)
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
@extend .zIndex-5; // has to be behind modal windows and beneath notifications (popover)
|
||||
position: absolute;
|
||||
|
||||
@@ -13,7 +13,7 @@ class Integration::ExchangeController < ApplicationController
|
||||
)
|
||||
|
||||
{
|
||||
endpoint: client.autodiscover.ews_url,
|
||||
endpoint: client.try(:autodiscover).try(:ews_url),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -187,46 +187,50 @@ class ReportsController < ApplicationController
|
||||
worksheet.set_row(0, 0, 6)
|
||||
|
||||
# Write a formatted and unformatted string, row and column notation.
|
||||
worksheet.write(0, 0, "Tickets: #{profile.name} (#{title})", format)
|
||||
worksheet.write_string(0, 0, "Tickets: #{profile.name} (#{title})", format)
|
||||
|
||||
format_header = workbook.add_format
|
||||
format_header.set_italic
|
||||
format_header.set_bg_color('gray')
|
||||
format_header.set_color('white')
|
||||
|
||||
worksheet.write(2, 0, '#', format_header)
|
||||
worksheet.write(2, 1, 'Title', format_header)
|
||||
worksheet.write(2, 2, 'State', format_header)
|
||||
worksheet.write(2, 3, 'Priority', format_header)
|
||||
worksheet.write(2, 4, 'Group', format_header)
|
||||
worksheet.write(2, 5, 'Owner', format_header)
|
||||
worksheet.write(2, 6, 'Customer', format_header)
|
||||
worksheet.write(2, 7, 'Organization', format_header)
|
||||
worksheet.write(2, 8, 'Create Channel', format_header)
|
||||
worksheet.write(2, 9, 'Sender', format_header)
|
||||
worksheet.write(2, 10, 'Tags', format_header)
|
||||
worksheet.write(2, 11, 'Created at', format_header)
|
||||
worksheet.write(2, 12, 'Updated at', format_header)
|
||||
worksheet.write(2, 13, 'Closed at', format_header)
|
||||
worksheet.write_string(2, 0, '#', format_header)
|
||||
worksheet.write_string(2, 1, 'Title', format_header)
|
||||
worksheet.write_string(2, 2, 'State', format_header)
|
||||
worksheet.write_string(2, 3, 'Priority', format_header)
|
||||
worksheet.write_string(2, 4, 'Group', format_header)
|
||||
worksheet.write_string(2, 5, 'Owner', format_header)
|
||||
worksheet.write_string(2, 6, 'Customer', format_header)
|
||||
worksheet.write_string(2, 7, 'Organization', format_header)
|
||||
worksheet.write_string(2, 8, 'Create Channel', format_header)
|
||||
worksheet.write_string(2, 9, 'Sender', format_header)
|
||||
worksheet.write_string(2, 10, 'Tags', format_header)
|
||||
worksheet.write_string(2, 11, 'Created at', format_header)
|
||||
worksheet.write_string(2, 12, 'Updated at', format_header)
|
||||
worksheet.write_string(2, 13, 'Closed at', format_header)
|
||||
|
||||
row = 2
|
||||
result[:ticket_ids].each do |ticket_id|
|
||||
ticket = Ticket.lookup(id: ticket_id)
|
||||
row += 1
|
||||
worksheet.write(row, 0, ticket.number)
|
||||
worksheet.write(row, 1, ticket.title)
|
||||
worksheet.write(row, 2, ticket.state.name)
|
||||
worksheet.write(row, 3, ticket.priority.name)
|
||||
worksheet.write(row, 4, ticket.group.name)
|
||||
worksheet.write(row, 5, ticket.owner.fullname)
|
||||
worksheet.write(row, 6, ticket.customer.fullname)
|
||||
worksheet.write(row, 7, ticket.try(:organization).try(:name))
|
||||
worksheet.write(row, 8, ticket.create_article_type.name)
|
||||
worksheet.write(row, 9, ticket.create_article_sender.name)
|
||||
worksheet.write(row, 10, ticket.tag_list.join(','))
|
||||
worksheet.write(row, 11, ticket.created_at)
|
||||
worksheet.write(row, 12, ticket.updated_at)
|
||||
worksheet.write(row, 13, ticket.close_at)
|
||||
begin
|
||||
ticket = Ticket.lookup(id: ticket_id)
|
||||
row += 1
|
||||
worksheet.write_string(row, 0, ticket.number)
|
||||
worksheet.write_string(row, 1, ticket.title)
|
||||
worksheet.write_string(row, 2, ticket.state.name)
|
||||
worksheet.write_string(row, 3, ticket.priority.name)
|
||||
worksheet.write_string(row, 4, ticket.group.name)
|
||||
worksheet.write_string(row, 5, ticket.owner.fullname)
|
||||
worksheet.write_string(row, 6, ticket.customer.fullname)
|
||||
worksheet.write_string(row, 7, ticket.try(:organization).try(:name))
|
||||
worksheet.write_string(row, 8, ticket.create_article_type.name)
|
||||
worksheet.write_string(row, 9, ticket.create_article_sender.name)
|
||||
worksheet.write_string(row, 10, ticket.tag_list.join(','))
|
||||
worksheet.write_date_time(row, 11, ticket.created_at.to_time.iso8601)
|
||||
worksheet.write_date_time(row, 12, ticket.updated_at.to_time.iso8601)
|
||||
worksheet.write_date_time(row, 13, ticket.close_at.to_time.iso8601)
|
||||
rescue => e
|
||||
Rails.logger.error "SKIP: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
workbook.close
|
||||
|
||||
@@ -57,7 +57,7 @@ class SearchController < ApplicationController
|
||||
}
|
||||
|
||||
# do only one query to index search backend
|
||||
if !objects_with_direct_search_index.empty?
|
||||
if objects_with_direct_search_index.present?
|
||||
items = SearchIndexBackend.search(query, limit, objects_with_direct_search_index)
|
||||
items.each { |item|
|
||||
require item[:type].to_filename
|
||||
@@ -71,7 +71,7 @@ class SearchController < ApplicationController
|
||||
# e. g. do ticket query by Ticket class to handle ticket permissions
|
||||
objects_without_direct_search_index.each { |object|
|
||||
object_result = search_generic_backend(object, query, limit, current_user, assets)
|
||||
if !object_result.empty?
|
||||
if object_result.present?
|
||||
result = result.concat(object_result)
|
||||
end
|
||||
}
|
||||
@@ -92,7 +92,7 @@ class SearchController < ApplicationController
|
||||
# do query
|
||||
objects_in_order.each { |object|
|
||||
object_result = search_generic_backend(object, query, limit, current_user, assets)
|
||||
if !object_result.empty?
|
||||
if object_result.present?
|
||||
result = result.concat(object_result)
|
||||
end
|
||||
}
|
||||
|
||||
@@ -10,35 +10,36 @@ class TaskbarController < ApplicationController
|
||||
|
||||
def show
|
||||
taskbar = Taskbar.find(params[:id])
|
||||
access(taskbar)
|
||||
model_show_render_item(taskbar)
|
||||
access_to_taskbar(taskbar)
|
||||
model_create_render(Taskbar, params)
|
||||
end
|
||||
|
||||
def create
|
||||
task_user(params)
|
||||
model_create_render(Taskbar, params)
|
||||
end
|
||||
|
||||
def update
|
||||
taskbar = Taskbar.find(params[:id])
|
||||
access(taskbar)
|
||||
taskbar.with_lock do
|
||||
taskbar.update_attributes!(Taskbar.param_cleanup(params))
|
||||
end
|
||||
model_update_render_item(taskbar)
|
||||
access_to_taskbar(taskbar)
|
||||
task_user(params)
|
||||
model_update_render(Taskbar, params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
taskbar = Taskbar.find(params[:id])
|
||||
access(taskbar)
|
||||
taskbar.with_lock do
|
||||
taskbar.destroy
|
||||
end
|
||||
model_destroy_render_item()
|
||||
access_to_taskbar(taskbar)
|
||||
model_destroy_render(Taskbar, params)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def access(taskbar)
|
||||
def access_to_taskbar(taskbar)
|
||||
raise Exceptions::UnprocessableEntity, 'Not allowed to access this task.' if taskbar.user_id != current_user.id
|
||||
end
|
||||
|
||||
def task_user(params)
|
||||
params[:user_id] = current_user.id
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -382,7 +382,7 @@ class TimeAccountingsController < ApplicationController
|
||||
worksheet.set_row(0, 0, header.count)
|
||||
|
||||
# Write a formatted and unformatted string, row and column notation.
|
||||
worksheet.write(0, 0, title, format)
|
||||
worksheet.write_string(0, 0, title, format)
|
||||
|
||||
format_header = workbook.add_format # Add a format
|
||||
format_header.set_italic
|
||||
@@ -393,7 +393,7 @@ class TimeAccountingsController < ApplicationController
|
||||
if item[:width]
|
||||
worksheet.set_column(count, count, item[:width])
|
||||
end
|
||||
worksheet.write(2, count, item[:name], format_header)
|
||||
worksheet.write_string(2, count, item[:name], format_header)
|
||||
count += 1
|
||||
}
|
||||
|
||||
@@ -403,6 +403,11 @@ class TimeAccountingsController < ApplicationController
|
||||
row_item_count = 0
|
||||
row.each { |item|
|
||||
worksheet.write(row_count, row_item_count, item)
|
||||
if item.acts_like?(:date)
|
||||
worksheet.write_date_time(row_count, row_item_count, item.to_time.iso8601)
|
||||
else
|
||||
worksheet.write_string(row_count, row_item_count, item)
|
||||
end
|
||||
row_item_count += 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,8 +368,9 @@ class UsersController < ApplicationController
|
||||
limit: params[:limit],
|
||||
current_user: current_user,
|
||||
}
|
||||
if params[:role_ids].present?
|
||||
query_params[:role_ids] = params[:role_ids]
|
||||
[:role_ids, :permissions].each do |key|
|
||||
next if params[key].blank?
|
||||
query_params[key] = params[key]
|
||||
end
|
||||
|
||||
# do query
|
||||
|
||||
@@ -15,6 +15,7 @@ class Channel < ApplicationModel
|
||||
|
||||
# rubocop:disable Style/ClassVars
|
||||
@@channel_stream = {}
|
||||
@@channel_stream_started_till_at = {}
|
||||
# rubocop:enable Style/ClassVars
|
||||
|
||||
=begin
|
||||
@@ -63,7 +64,7 @@ fetch one account
|
||||
self.status_in = result[:result]
|
||||
self.last_log_in = result[:notice]
|
||||
preferences[:last_fetch] = Time.zone.now
|
||||
save
|
||||
save!
|
||||
rescue => e
|
||||
error = "Can't use Channel::Driver::#{adapter.to_classname}: #{e.inspect}"
|
||||
logger.error error
|
||||
@@ -71,7 +72,7 @@ fetch one account
|
||||
self.status_in = 'error'
|
||||
self.last_log_in = error
|
||||
preferences[:last_fetch] = Time.zone.now
|
||||
save
|
||||
save!
|
||||
end
|
||||
|
||||
end
|
||||
@@ -116,7 +117,7 @@ stream instance of account
|
||||
logger.error e.backtrace
|
||||
self.status_in = 'error'
|
||||
self.last_log_in = error
|
||||
save
|
||||
save!
|
||||
end
|
||||
|
||||
end
|
||||
@@ -132,7 +133,7 @@ stream all accounts
|
||||
def self.stream
|
||||
Thread.abort_on_exception = true
|
||||
|
||||
auto_reconnect_after = 25
|
||||
auto_reconnect_after = 180
|
||||
last_channels = []
|
||||
|
||||
loop do
|
||||
@@ -140,12 +141,22 @@ stream all accounts
|
||||
|
||||
current_channels = []
|
||||
channels = Channel.where('active = ? AND area LIKE ?', true, '%::Account')
|
||||
channels.each { |channel|
|
||||
next if channel.options[:adapter] != 'twitter'
|
||||
channels.each do |channel|
|
||||
adapter = channel.options[:adapter]
|
||||
next if adapter.blank?
|
||||
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
||||
next if !driver_class.respond_to?(:streamable?)
|
||||
next if !driver_class.streamable?
|
||||
channel_id = channel.id.to_s
|
||||
|
||||
if @@channel_stream[channel_id].blank? && @@channel_stream_started_till_at[channel_id] && @@channel_stream_started_till_at[channel_id] > Time.zone.now - 65.seconds
|
||||
logger.info "skipp channel (#{channel_id}) for streaming, already tried to connect or connection was active within the last minute"
|
||||
next
|
||||
end
|
||||
|
||||
current_channels.push channel_id
|
||||
|
||||
# exit it channel has changed or connection is older then 25 min.
|
||||
# exit it channel has changed or connection is older then 180 minutes
|
||||
if @@channel_stream[channel_id]
|
||||
if @@channel_stream[channel_id][:updated_at] != channel.updated_at
|
||||
logger.info "channel (#{channel.id}) has changed, stop thread"
|
||||
@@ -153,12 +164,14 @@ stream all accounts
|
||||
@@channel_stream[channel_id][:thread].join
|
||||
@@channel_stream[channel_id][:stream_instance].disconnect
|
||||
@@channel_stream[channel_id] = false
|
||||
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
||||
elsif @@channel_stream[channel_id][:started_at] && @@channel_stream[channel_id][:started_at] < Time.zone.now - auto_reconnect_after.minutes
|
||||
logger.info "channel (#{channel.id}) reconnect - thread is older then #{auto_reconnect_after} minutes, restart thread"
|
||||
@@channel_stream[channel_id][:thread].exit
|
||||
@@channel_stream[channel_id][:thread].join
|
||||
@@channel_stream[channel_id][:stream_instance].disconnect
|
||||
@@channel_stream[channel_id] = false
|
||||
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
||||
end
|
||||
end
|
||||
|
||||
@@ -177,34 +190,41 @@ stream all accounts
|
||||
@@channel_stream[channel_id][:thread] = Thread.new {
|
||||
begin
|
||||
logger.info "Started stream channel for '#{channel.id}' (#{channel.area})..."
|
||||
channel.status_in = 'ok'
|
||||
channel.last_log_in = ''
|
||||
channel.save!
|
||||
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
||||
@@channel_stream[channel_id] ||= {}
|
||||
@@channel_stream[channel_id][:stream_instance] = channel.stream_instance
|
||||
@@channel_stream[channel_id][:stream_instance].stream
|
||||
@@channel_stream[channel_id][:stream_instance].disconnect
|
||||
@@channel_stream[channel_id] = false
|
||||
logger.info " ...stopped thread for '#{channel.id}'"
|
||||
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
||||
logger.info " ...stopped stream thread for '#{channel.id}'"
|
||||
rescue => e
|
||||
error = "Can't use channel (#{channel.id}): #{e.inspect}"
|
||||
error = "Can't use stream for channel (#{channel.id}): #{e.inspect}"
|
||||
logger.error error
|
||||
logger.error e.backtrace
|
||||
channel.status_in = 'error'
|
||||
channel.last_log_in = error
|
||||
channel.save
|
||||
channel.save!
|
||||
@@channel_stream[channel_id] = false
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# cleanup deleted channels
|
||||
last_channels.each { |channel_id|
|
||||
next if !@@channel_stream[channel_id.to_s]
|
||||
last_channels.each do |channel_id|
|
||||
next if @@channel_stream[channel_id].blank?
|
||||
next if current_channels.include?(channel_id)
|
||||
logger.info "channel (#{channel_id}) not longer active, stop thread"
|
||||
@@channel_stream[channel_id.to_s][:thread].exit
|
||||
@@channel_stream[channel_id.to_s][:thread].join
|
||||
@@channel_stream[channel_id.to_s][:stream_instance].disconnect
|
||||
@@channel_stream[channel_id.to_s] = false
|
||||
}
|
||||
logger.info "channel (#{channel_id}) not longer active, stop stream thread"
|
||||
@@channel_stream[channel_id][:thread].exit
|
||||
@@channel_stream[channel_id][:thread].join
|
||||
@@channel_stream[channel_id][:stream_instance].disconnect
|
||||
@@channel_stream[channel_id] = false
|
||||
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
||||
end
|
||||
|
||||
last_channels = current_channels
|
||||
|
||||
sleep 20
|
||||
@@ -245,14 +265,14 @@ send via account
|
||||
result = driver_instance.send(adapter_options, mail_params, notification)
|
||||
self.status_out = 'ok'
|
||||
self.last_log_out = ''
|
||||
save
|
||||
save!
|
||||
rescue => e
|
||||
error = "Can't use Channel::Driver::#{adapter.to_classname}: #{e.inspect}"
|
||||
logger.error error
|
||||
logger.error e.backtrace
|
||||
self.status_out = 'error'
|
||||
self.last_log_out = error
|
||||
save
|
||||
save!
|
||||
raise error
|
||||
end
|
||||
result
|
||||
|
||||
@@ -63,6 +63,20 @@ class Channel::Driver::Facebook
|
||||
def disconnect
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
Channel::Driver::Facebook.streamable?
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.streamable?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_page(page_id)
|
||||
|
||||
@@ -60,7 +60,7 @@ example
|
||||
|
||||
=end
|
||||
|
||||
def fetch (options, channel, check_type = '', verify_string = '')
|
||||
def fetch(options, channel, check_type = '', verify_string = '')
|
||||
ssl = true
|
||||
port = 993
|
||||
keep_on_server = false
|
||||
@@ -224,6 +224,20 @@ example
|
||||
@imap.disconnect()
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
Channel::Driver::Imap.streamable?
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.streamable?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def already_imported?(message_id, message_meta, count, count_all, keep_on_server)
|
||||
|
||||
@@ -44,7 +44,7 @@ returns
|
||||
|
||||
=end
|
||||
|
||||
def fetch (options, channel, check_type = '', verify_string = '')
|
||||
def fetch(options, channel, check_type = '', verify_string = '')
|
||||
ssl = true
|
||||
port = 995
|
||||
if options.key?(:ssl) && options[:ssl] == false
|
||||
@@ -180,6 +180,20 @@ returns
|
||||
true
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
Channel::Driver::Pop3.streamable?
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.streamable?
|
||||
false
|
||||
end
|
||||
|
||||
def disconnect
|
||||
return if !@pop
|
||||
@pop.finish
|
||||
|
||||
@@ -30,6 +30,20 @@ class Channel::Driver::Telegram
|
||||
message
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
Channel::Driver::Telegram.streamable?
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.streamable?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_external_credential(options)
|
||||
|
||||
@@ -123,6 +123,20 @@ returns
|
||||
@rest_client.disconnect if @rest_client
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
Channel::Driver::Twitter.streamable?
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.streamable?
|
||||
true
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
create stream endpoint form twitter account
|
||||
@@ -183,8 +197,8 @@ returns
|
||||
=end
|
||||
|
||||
def stream
|
||||
sleep_on_unauthorized = 61
|
||||
2.times { |loop_count|
|
||||
sleep_on_unauthorized = 65
|
||||
2.times do |loop_count|
|
||||
begin
|
||||
stream_start
|
||||
rescue Twitter::Error::Unauthorized => e
|
||||
@@ -196,7 +210,7 @@ returns
|
||||
raise "Unable to stream, try #{loop_count}, error #{e.inspect}"
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def stream_start
|
||||
|
||||
@@ -475,7 +475,7 @@ returns
|
||||
# check ignore header
|
||||
if mail['x-zammad-ignore'.to_sym] == 'true' || mail['x-zammad-ignore'.to_sym] == true
|
||||
Rails.logger.info "ignored email with msgid '#{mail[:message_id]}' from '#{mail[:from]}' because of x-zammad-ignore header"
|
||||
return true
|
||||
return
|
||||
end
|
||||
|
||||
# set interface handle
|
||||
|
||||
@@ -18,7 +18,8 @@ class Channel::Filter::MonitoringBase
|
||||
sender = Setting.get("#{integration}_sender")
|
||||
auto_close = Setting.get("#{integration}_auto_close")
|
||||
auto_close_state_id = Setting.get("#{integration}_auto_close_state_id")
|
||||
state_recovery_match = '(OK|UP)'
|
||||
state_ignore_match = Setting.get("#{integration}_ignore_match") || ''
|
||||
state_recovery_match = Setting.get("#{integration}_recovery_match") || '(OK|UP)'
|
||||
|
||||
return if mail[:from].blank?
|
||||
return if mail[:body].blank?
|
||||
@@ -26,7 +27,7 @@ class Channel::Filter::MonitoringBase
|
||||
return if !session_user_id
|
||||
|
||||
# check if sender is monitoring
|
||||
return if !mail[:from].match(/#{Regexp.quote(sender)}/i)
|
||||
return if !Channel::Filter::Database.match(mail[:from], sender, true, true)
|
||||
|
||||
# get mail attibutes like host and state
|
||||
result = {}
|
||||
@@ -52,6 +53,13 @@ class Channel::Filter::MonitoringBase
|
||||
end
|
||||
end
|
||||
|
||||
# get state from subject
|
||||
if result['state'].blank?
|
||||
if mail[:subject] =~ /on\s.+?\sis\s(.+?)\!/
|
||||
result['state'] = $1
|
||||
end
|
||||
end
|
||||
|
||||
# check if ticket with host is open
|
||||
customer = User.lookup(id: session_user_id)
|
||||
|
||||
@@ -72,6 +80,7 @@ class Channel::Filter::MonitoringBase
|
||||
|
||||
# check if service is recovered
|
||||
if auto_close && result['state'].present? && result['state'].match(/#{state_recovery_match}/i)
|
||||
Rails.logger.info "MonitoringBase.#{integration} set autoclose to state_id #{auto_close_state_id}"
|
||||
state = Ticket::State.lookup(id: auto_close_state_id)
|
||||
if state
|
||||
mail[ 'x-zammad-ticket-followup-state'.to_sym ] = state.name
|
||||
@@ -91,5 +100,18 @@ class Channel::Filter::MonitoringBase
|
||||
mail[ 'x-zammad-ticket-preferences'.to_sym ][key] = value
|
||||
}
|
||||
end
|
||||
|
||||
# ignorte states
|
||||
if state_ignore_match.present? && result['state'].present? && result['state'].match(/#{state_ignore_match}/i)
|
||||
mail[ 'x-zammad-ignore'.to_sym ] = true
|
||||
return true
|
||||
end
|
||||
|
||||
# if now problem exists, just ignore the email
|
||||
if result['state'].present? && result['state'].match(/#{state_recovery_match}/i)
|
||||
mail[ 'x-zammad-ignore'.to_sym ] = true
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,10 +19,10 @@ update search index, if configured - will be executed automatically
|
||||
=end
|
||||
|
||||
def search_index_update
|
||||
return if ignore_search_indexing?(:update)
|
||||
return true if ignore_search_indexing?(:update)
|
||||
|
||||
# start background job to transfer data to search index
|
||||
return if !SearchIndexBackend.enabled?
|
||||
return true if !SearchIndexBackend.enabled?
|
||||
Delayed::Job.enqueue(BackgroundJobSearchIndex.new(self.class.to_s, id))
|
||||
true
|
||||
end
|
||||
@@ -37,7 +37,7 @@ delete search index object, will be executed automatically
|
||||
=end
|
||||
|
||||
def search_index_destroy
|
||||
return if ignore_search_indexing?(:destroy)
|
||||
return true if ignore_search_indexing?(:destroy)
|
||||
SearchIndexBackend.remove(self.class.to_s, id)
|
||||
true
|
||||
end
|
||||
@@ -58,7 +58,7 @@ returns
|
||||
def search_index_update_backend
|
||||
# fill up with search data
|
||||
attributes = search_index_attribute_lookup
|
||||
return if !attributes
|
||||
return true if !attributes
|
||||
|
||||
# update backend
|
||||
SearchIndexBackend.add(self.class.to_s, attributes)
|
||||
@@ -86,10 +86,10 @@ returns
|
||||
attributes = {}
|
||||
%w(name note).each { |key|
|
||||
next if !self[key]
|
||||
next if self[key].respond_to?('empty?') && self[key].empty?
|
||||
next if self[key].respond_to?('blank?') && self[key].blank?
|
||||
attributes[key] = self[key]
|
||||
}
|
||||
return if attributes.empty?
|
||||
return true if attributes.blank?
|
||||
attributes
|
||||
end
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ class Observer::Ticket::ResetNewState < ActiveRecord::Observer
|
||||
# return if we run import mode
|
||||
return if Setting.get('import_mode')
|
||||
|
||||
# only change state if not processed via postmaster
|
||||
return if ApplicationHandleInfo.current.split('.')[1] == 'postmaster'
|
||||
|
||||
# if article in internal
|
||||
return true if record.internal
|
||||
|
||||
|
||||
@@ -47,13 +47,15 @@ class Observer::User::Geo < ActiveRecord::Observer
|
||||
address = ''
|
||||
location = %w(address street zip city country)
|
||||
location.each { |item|
|
||||
if record[item] && record[item] != ''
|
||||
address = address + ',' + record[item]
|
||||
next if record[item].blank?
|
||||
if address.present?
|
||||
address += ', '
|
||||
end
|
||||
address += record[item]
|
||||
}
|
||||
|
||||
# return if no address is given
|
||||
return if address == ''
|
||||
return if address.blank?
|
||||
|
||||
# lookup
|
||||
latlng = Service::GeoLocation.geocode(address)
|
||||
|
||||
@@ -549,6 +549,12 @@ condition example
|
||||
# get attributes
|
||||
attributes = attribute.split(/\./)
|
||||
attribute = "#{attributes[0]}s.#{attributes[1]}"
|
||||
|
||||
# magic selectors
|
||||
if attributes[0] == 'ticket' && attributes[1] == 'out_of_office_replacement_id'
|
||||
attribute = "#{attributes[0]}s.owner_id"
|
||||
end
|
||||
|
||||
if attributes[0] == 'ticket' && attributes[1] == 'tags'
|
||||
selector['value'] = selector['value'].split(/,/).collect(&:strip)
|
||||
end
|
||||
@@ -567,8 +573,13 @@ condition example
|
||||
end
|
||||
elsif selector['pre_condition'] == 'current_user.id'
|
||||
raise "Use current_user.id in selector, but no current_user is set #{selector.inspect}" if !current_user_id
|
||||
query += "#{attribute} IN (?)"
|
||||
bind_params.push current_user_id
|
||||
if attributes[1] == 'out_of_office_replacement_id'
|
||||
query += "#{attribute} IN (?)"
|
||||
bind_params.push User.find(current_user_id).out_of_office_agent_of.pluck(:id)
|
||||
else
|
||||
query += "#{attribute} IN (?)"
|
||||
bind_params.push current_user_id
|
||||
end
|
||||
elsif selector['pre_condition'] == 'current_user.organization_id'
|
||||
raise "Use current_user.id in selector, but no current_user is set #{selector.inspect}" if !current_user_id
|
||||
query += "#{attribute} IN (?)"
|
||||
@@ -579,8 +590,13 @@ condition example
|
||||
if selector['value'].nil?
|
||||
query += "#{attribute} IS NULL"
|
||||
else
|
||||
query += "#{attribute} IN (?)"
|
||||
bind_params.push selector['value']
|
||||
if attributes[1] == 'out_of_office_replacement_id'
|
||||
query += "#{attribute} IN (?)"
|
||||
bind_params.push User.find(selector['value']).out_of_office_agent_of.pluck(:id)
|
||||
else
|
||||
query += "#{attribute} IN (?)"
|
||||
bind_params.push selector['value']
|
||||
end
|
||||
end
|
||||
# rubocop:enable Style/IfInsideElse
|
||||
end
|
||||
@@ -593,8 +609,13 @@ condition example
|
||||
query += "#{attribute} IS NOT NULL"
|
||||
end
|
||||
elsif selector['pre_condition'] == 'current_user.id'
|
||||
query += "#{attribute} NOT IN (?)"
|
||||
bind_params.push current_user_id
|
||||
if attributes[1] == 'out_of_office_replacement_id'
|
||||
query += "#{attribute} NOT IN (?)"
|
||||
bind_params.push User.find(current_user_id).out_of_office_agent_of.pluck(:id)
|
||||
else
|
||||
query += "#{attribute} NOT IN (?)"
|
||||
bind_params.push current_user_id
|
||||
end
|
||||
elsif selector['pre_condition'] == 'current_user.organization_id'
|
||||
query += "#{attribute} NOT IN (?)"
|
||||
user = User.lookup(id: current_user_id)
|
||||
@@ -604,8 +625,13 @@ condition example
|
||||
if selector['value'].nil?
|
||||
query += "#{attribute} IS NOT NULL"
|
||||
else
|
||||
query += "#{attribute} NOT IN (?)"
|
||||
bind_params.push selector['value']
|
||||
if attributes[1] == 'out_of_office_replacement_id'
|
||||
query += "#{attribute} NOT IN (?)"
|
||||
bind_params.push User.find(selector['value']).out_of_office_agent_of.pluck(:id)
|
||||
else
|
||||
query += "#{attribute} NOT IN (?)"
|
||||
bind_params.push selector['value']
|
||||
end
|
||||
end
|
||||
# rubocop:enable Style/IfInsideElse
|
||||
end
|
||||
|
||||
@@ -16,8 +16,8 @@ class Ticket::Article < ApplicationModel
|
||||
belongs_to :created_by, class_name: 'User'
|
||||
belongs_to :updated_by, class_name: 'User'
|
||||
store :preferences
|
||||
before_create :check_subject, :check_message_id_md5
|
||||
before_update :check_subject, :check_message_id_md5
|
||||
before_create :check_subject, :check_body, :check_message_id_md5
|
||||
before_update :check_subject, :check_body, :check_message_id_md5
|
||||
|
||||
sanitized_html :body
|
||||
|
||||
@@ -284,6 +284,22 @@ returns
|
||||
true
|
||||
end
|
||||
|
||||
# strip body length or raise exception
|
||||
def check_body
|
||||
return true if body.blank?
|
||||
limit = 1_500_000
|
||||
current_length = body.length
|
||||
if body.length > limit
|
||||
if ApplicationHandleInfo.current.present? && ApplicationHandleInfo.current.split('.')[1] == 'postmaster'
|
||||
logger.warn "WARNING: cut string because of database length #{self.class}.body(#{limit} but is #{current_length})"
|
||||
self.body = body[0, limit]
|
||||
else
|
||||
raise Exceptions::UnprocessableEntity, "body if article is to large, #{current_length} chars - only #{limit} allowed"
|
||||
end
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def history_log_attributes
|
||||
{
|
||||
related_o_id: self['ticket_id'],
|
||||
|
||||
@@ -34,18 +34,18 @@ returns
|
||||
# collect article data
|
||||
articles = Ticket::Article.where(ticket_id: id)
|
||||
attributes['article'] = []
|
||||
articles.each { |article|
|
||||
article_attributes = article.attributes
|
||||
|
||||
# remove note needed attributes
|
||||
ignore = %w(message_id_md5)
|
||||
ignore.each { |attribute|
|
||||
article_attributes.delete(attribute)
|
||||
}
|
||||
articles.each do |article|
|
||||
|
||||
# lookup attributes of ref. objects (normally name and note)
|
||||
article_attributes = article.search_index_attribute_lookup
|
||||
|
||||
# remove note needed attributes
|
||||
ignore = %w(message_id_md5 ticket)
|
||||
ignore.each do |attribute|
|
||||
article_attributes.delete(attribute)
|
||||
end
|
||||
|
||||
# index raw text body
|
||||
if article_attributes['content_type'] && article_attributes['content_type'] == 'text/html' && article_attributes['body']
|
||||
article_attributes['body'] = article_attributes['body'].html2text
|
||||
@@ -76,7 +76,7 @@ returns
|
||||
article_attributes['attachment'].push data
|
||||
}
|
||||
attributes['article'].push article_attributes
|
||||
}
|
||||
end
|
||||
|
||||
attributes
|
||||
end
|
||||
|
||||
@@ -199,6 +199,23 @@ returns
|
||||
|
||||
=begin
|
||||
|
||||
gets users where user is replacement
|
||||
|
||||
user = User.find(123)
|
||||
result = user.out_of_office_agent_of
|
||||
|
||||
returns
|
||||
|
||||
result = [user_model1, user_model2]
|
||||
|
||||
=end
|
||||
|
||||
def out_of_office_agent_of
|
||||
User.where(active: true, out_of_office: true, out_of_office_replacement_id: id).where('out_of_office_start_at <= ? AND out_of_office_end_at >= ?', Time.zone.today, Time.zone.today)
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
get users activity stream
|
||||
|
||||
user = User.find(123)
|
||||
|
||||
@@ -40,6 +40,16 @@ search user
|
||||
current_user: user_model,
|
||||
)
|
||||
|
||||
or with certain role_ids | permissions
|
||||
|
||||
result = User.search(
|
||||
query: 'some search term',
|
||||
limit: 15,
|
||||
current_user: user_model,
|
||||
role_ids: [1,2,3],
|
||||
permissions: ['ticket.agent']
|
||||
)
|
||||
|
||||
returns
|
||||
|
||||
result = [user_model1, user_model2, ...]
|
||||
@@ -56,9 +66,28 @@ returns
|
||||
# enable search only for agents and admins
|
||||
return [] if !search_preferences(current_user)
|
||||
|
||||
# lookup for roles of permission
|
||||
if params[:permissions].present?
|
||||
params[:role_ids] ||= []
|
||||
role_ids = Role.with_permissions(params[:permissions]).map(&:id)
|
||||
params[:role_ids].concat(role_ids)
|
||||
end
|
||||
|
||||
# try search index backend
|
||||
if SearchIndexBackend.enabled?
|
||||
items = SearchIndexBackend.search(query, limit, 'User')
|
||||
query_extention = {}
|
||||
if params[:role_ids].present?
|
||||
query_extention['bool'] = {}
|
||||
query_extention['bool']['must'] = []
|
||||
if !params[:role_ids].is_a?(Array)
|
||||
params[:role_ids] = [params[:role_ids]]
|
||||
end
|
||||
access_condition = {
|
||||
'query_string' => { 'default_field' => 'role_ids', 'query' => "\"#{params[:role_ids].join('" OR "')}\"" }
|
||||
}
|
||||
query_extention['bool']['must'].push access_condition
|
||||
end
|
||||
items = SearchIndexBackend.search(query, limit, 'User', query_extention)
|
||||
users = []
|
||||
items.each { |item|
|
||||
user = User.lookup(id: item[:id])
|
||||
|
||||
@@ -5,6 +5,33 @@ class User
|
||||
|
||||
=begin
|
||||
|
||||
lookup name of ref. objects
|
||||
|
||||
user = User.find(123)
|
||||
attributes = user.search_index_attribute_lookup
|
||||
|
||||
returns
|
||||
|
||||
attributes # object with lookup data
|
||||
|
||||
=end
|
||||
|
||||
def search_index_attribute_lookup
|
||||
attributes = super
|
||||
|
||||
attributes['permissions'] = []
|
||||
permissions_with_child_ids.each do |permission_id|
|
||||
permission = ::Permission.lookup(id: permission_id)
|
||||
next if !permission
|
||||
attributes['permissions'].push permission.name
|
||||
end
|
||||
attributes['role_ids'] = role_ids
|
||||
|
||||
attributes
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
get data to store in search index
|
||||
|
||||
user = User.find(2)
|
||||
@@ -30,12 +57,12 @@ returns
|
||||
next if key == 'preferences'
|
||||
next if key == 'password'
|
||||
next if !value
|
||||
next if value.respond_to?('empty?') && value.empty?
|
||||
next if value.respond_to?('blank?') && value.blank?
|
||||
attributes[key] = value
|
||||
}
|
||||
return if attributes.empty?
|
||||
return if attributes.blank?
|
||||
|
||||
if attributes['organization_id']
|
||||
if attributes['organization_id'].present?
|
||||
organization = Organization.lookup(id: attributes['organization_id'])
|
||||
if organization
|
||||
attributes['organization'] = organization.name
|
||||
|
||||
@@ -11,4 +11,6 @@ set -e
|
||||
contrib/packager.io/fetch_locales.rb
|
||||
|
||||
# delete asset cache
|
||||
rm -r tmp/cache
|
||||
rm -r tmp/cache
|
||||
|
||||
rm -r vendor/bundle/ruby/2.3.0 || true
|
||||
|
||||
18
contrib/travis-ci.org/trigger-docker-build.sh
Executable file
18
contrib/travis-ci.org/trigger-docker-build.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# trigger build of https://github.com/zammad/zammad-docker on https://travis-ci.org/zammad/zammad-docker and upload it to https://hub.docker.com/r/zammad/zammad
|
||||
#
|
||||
|
||||
REPO_USER="zammad"
|
||||
REPO="zammad-docker"
|
||||
BRANCH="master"
|
||||
|
||||
if [ "${TRAVIS_BRANCH}" == 'develop' ]; then
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Travis-API-Version: 3" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: token ${TRAVIS_API_TOKEN}" \
|
||||
-d '{"request":{ "message": "'"${TRAVIS_COMMIT_MESSAGE}"'","branch":"'${BRANCH}'","config":{"env":{"ZAMMAD_VERSION":"'${ZAMMAD_VERSION}'"}}}}' \
|
||||
"https://api.travis-ci.org/repo/${REPO_USER}%2F${REPO}/requests"
|
||||
fi
|
||||
@@ -7,12 +7,12 @@ REPO_USER="zammad"
|
||||
REPO="zammad-docker-compose"
|
||||
BRANCH="master"
|
||||
|
||||
if [ ${TRAVIS_BRANCH} == 'stable' ]; then
|
||||
if [ "${TRAVIS_BRANCH}" == 'stable' ]; then
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Travis-API-Version: 3" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: token ${TRAVIS_API_TOKEN}" \
|
||||
-d '{"request": {"branch":"'${BRANCH}'"}}' \
|
||||
-d '{"request":{ "message": "'"${TRAVIS_COMMIT_MESSAGE}"'","branch":"'${BRANCH}'","config":{"env":{"ZAMMAD_VERSION":"'${ZAMMAD_VERSION}'"}}}}' \
|
||||
"https://api.travis-ci.org/repo/${REPO_USER}%2F${REPO}/requests"
|
||||
fi
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
class OutOfOffice < ActiveRecord::Migration
|
||||
def up
|
||||
|
||||
# return if it's a new setup
|
||||
return if !Setting.find_by(name: 'system_init_done')
|
||||
|
||||
add_column :overviews, :out_of_office, :boolean, null: false, default: false
|
||||
Overview.reset_column_information
|
||||
|
||||
role_ids = Role.with_permissions(['ticket.agent']).map(&:id)
|
||||
overview_role = Role.find_by(name: 'Agent')
|
||||
Overview.create_if_not_exists(
|
||||
name: 'My replacement Tickets',
|
||||
link: 'my_replacement_tickets',
|
||||
prio: 1080,
|
||||
role_ids: role_ids,
|
||||
out_of_office: true,
|
||||
condition: {
|
||||
'ticket.state_id' => {
|
||||
operator: 'is',
|
||||
value: Ticket::State.by_category(:open).pluck(:id),
|
||||
},
|
||||
#'ticket.out_of_office_replacement_id' => {
|
||||
# operator: 'is',
|
||||
# pre_condition: 'current_user.organization_id',
|
||||
#},
|
||||
},
|
||||
order: {
|
||||
by: 'created_at',
|
||||
direction: 'DESC',
|
||||
},
|
||||
view: {
|
||||
d: %w(title customer group owner escalation_at),
|
||||
s: %w(title customer group owner escalation_at),
|
||||
m: %w(number title customer group owner escalation_at),
|
||||
view_mode_default: 's',
|
||||
},
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
add_column :users, :out_of_office, :boolean, null: false, default: false
|
||||
add_column :users, :out_of_office_start_at, :date, null: true
|
||||
add_column :users, :out_of_office_end_at, :date, null: true
|
||||
add_column :users, :out_of_office_replacement_id, :integer, null: true
|
||||
|
||||
add_index :users, [:out_of_office, :out_of_office_start_at, :out_of_office_end_at], name: 'index_out_of_office'
|
||||
add_index :users, [:out_of_office_replacement_id]
|
||||
add_foreign_key :users, :users, column: :out_of_office_replacement_id
|
||||
|
||||
Cache.clear
|
||||
end
|
||||
end
|
||||
@@ -1,12 +1,12 @@
|
||||
class FixedTwitterTicketArticlePreferences3 < ActiveRecord::Migration
|
||||
class FixedTwitterTicketArticlePreferences4 < ActiveRecord::Migration
|
||||
def up
|
||||
|
||||
# return if it's a new setup
|
||||
return if !Setting.find_by(name: 'system_init_done')
|
||||
|
||||
# find article preferences with Twitter::NullObject and replace it with nill to prevent elasticsearch index issue
|
||||
article_type = Ticket::Article::Type.find_by(name: 'twitter status')
|
||||
article_ids = Ticket::Article.where(type_id: article_type.id).pluck(:id)
|
||||
article_type_ids = Ticket::Article::Type.where(name: ['twitter status', 'twitter direct-message']).pluck(:id)
|
||||
article_ids = Ticket::Article.where(type_id: article_type_ids).pluck(:id)
|
||||
article_ids.each { |article_id|
|
||||
article = Ticket::Article.find(article_id)
|
||||
next if !article.preferences
|
||||
58
db/migrate/20170910000002_out_of_office2.rb
Normal file
58
db/migrate/20170910000002_out_of_office2.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
class OutOfOffice2 < ActiveRecord::Migration
|
||||
def up
|
||||
|
||||
# return if it's a new setup
|
||||
return if !Setting.find_by(name: 'system_init_done')
|
||||
|
||||
if !ActiveRecord::Base.connection.column_exists?(:overviews, :out_of_office)
|
||||
add_column :overviews, :out_of_office, :boolean, null: false, default: false
|
||||
Overview.reset_column_information
|
||||
end
|
||||
|
||||
if !ActiveRecord::Base.connection.column_exists?(:users, :out_of_office)
|
||||
add_column :users, :out_of_office, :boolean, null: false, default: false
|
||||
add_column :users, :out_of_office_start_at, :date, null: true
|
||||
add_column :users, :out_of_office_end_at, :date, null: true
|
||||
add_column :users, :out_of_office_replacement_id, :integer, null: true
|
||||
|
||||
add_index :users, [:out_of_office, :out_of_office_start_at, :out_of_office_end_at], name: 'index_out_of_office'
|
||||
add_index :users, [:out_of_office_replacement_id]
|
||||
add_foreign_key :users, :users, column: :out_of_office_replacement_id
|
||||
User.reset_column_information
|
||||
end
|
||||
|
||||
role_ids = Role.with_permissions(['ticket.agent']).map(&:id)
|
||||
overview_role = Role.find_by(name: 'Agent')
|
||||
Overview.create_or_update(
|
||||
name: 'My replacement Tickets',
|
||||
link: 'my_replacement_tickets',
|
||||
prio: 1080,
|
||||
role_ids: role_ids,
|
||||
out_of_office: true,
|
||||
condition: {
|
||||
'ticket.state_id' => {
|
||||
operator: 'is',
|
||||
value: Ticket::State.by_category(:open).pluck(:id),
|
||||
},
|
||||
'ticket.out_of_office_replacement_id' => {
|
||||
operator: 'is',
|
||||
pre_condition: 'current_user.id',
|
||||
},
|
||||
},
|
||||
order: {
|
||||
by: 'created_at',
|
||||
direction: 'DESC',
|
||||
},
|
||||
view: {
|
||||
d: %w(title customer group owner escalation_at),
|
||||
s: %w(title customer group owner escalation_at),
|
||||
m: %w(number title customer group owner escalation_at),
|
||||
view_mode_default: 's',
|
||||
},
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
Cache.clear
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
class SettingSendNoAutoResponseRegExp < ActiveRecord::Migration
|
||||
def up
|
||||
|
||||
# return if it's a new setup
|
||||
return if !Setting.find_by(name: 'system_init_done')
|
||||
|
||||
# improved domain name matching
|
||||
return if !Setting.find_by(name: 'send_no_auto_response_reg_exp')
|
||||
Setting.set('send_no_auto_response_reg_exp', '(mailer-daemon|postmaster|abuse|root|noreply|noreply.+?|no-reply|no-reply.+?)@.+?')
|
||||
end
|
||||
|
||||
end
|
||||
@@ -171,10 +171,10 @@ Overview.create_if_not_exists(
|
||||
operator: 'is',
|
||||
value: Ticket::State.by_category(:open).pluck(:id),
|
||||
},
|
||||
#'ticket.out_of_office_replacement_id' => {
|
||||
# operator: 'is',
|
||||
# pre_condition: 'current_user.organization_id',
|
||||
#},
|
||||
'ticket.out_of_office_replacement_id' => {
|
||||
operator: 'is',
|
||||
pre_condition: 'current_user.id',
|
||||
},
|
||||
},
|
||||
order: {
|
||||
by: 'created_at',
|
||||
|
||||
@@ -32,6 +32,11 @@ module Import
|
||||
# when hitting the rate limit, sleep automatically,
|
||||
# then retry the request.
|
||||
config.retry = true
|
||||
|
||||
# disable cache to avoid unneeded memory consumption
|
||||
# since we are using each object only once
|
||||
# Inspired by: https://medium.com/swiftype-engineering/using-jmat-to-find-analyze-memory-in-jruby-1c4196c1ec72
|
||||
config.cache = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,6 +9,11 @@ class Service::Image::Zammad
|
||||
@@total_timeout = 6
|
||||
|
||||
def self.user(email)
|
||||
raise Exceptions::UnprocessableEntity, 'no email given' if email.blank?
|
||||
|
||||
email.downcase!
|
||||
|
||||
return if email =~ /@example.com$/
|
||||
|
||||
# fetch image
|
||||
response = UserAgent.post(
|
||||
@@ -35,10 +40,14 @@ class Service::Image::Zammad
|
||||
end
|
||||
|
||||
def self.organization(domain)
|
||||
raise Exceptions::UnprocessableEntity, 'no domain given' if domain.blank?
|
||||
|
||||
# strip, just use domain name
|
||||
domain = domain.sub(/^.+?@(.+?)$/, '\1')
|
||||
|
||||
domain.downcase!
|
||||
return if domain == 'example.com'
|
||||
|
||||
# fetch org logo
|
||||
response = UserAgent.post(
|
||||
"#{@@api_host}/api/v1/organization/image",
|
||||
|
||||
@@ -22,11 +22,6 @@ class Sessions::Backend::Collections::Base < Sessions::Backend::Base
|
||||
|
||||
def push
|
||||
|
||||
# check permission based access
|
||||
if self.class.permissions
|
||||
return if !@user.permissions?(self.class.permissions)
|
||||
end
|
||||
|
||||
# check timeout
|
||||
timeout = Sessions::CacheIn.get(client_key)
|
||||
return if timeout
|
||||
@@ -34,6 +29,11 @@ class Sessions::Backend::Collections::Base < Sessions::Backend::Base
|
||||
# set new timeout
|
||||
Sessions::CacheIn.set(client_key, true, { expires_in: @ttl.seconds })
|
||||
|
||||
# check permission based access
|
||||
if self.class.permissions
|
||||
return if !@user.permissions?(self.class.permissions)
|
||||
end
|
||||
|
||||
# check if update has been done
|
||||
last_change = self.class.model.constantize.latest_change
|
||||
return if last_change.to_s == @last_change
|
||||
|
||||
@@ -153,12 +153,12 @@ class TweetBase
|
||||
from = nil
|
||||
article_type = nil
|
||||
in_reply_to = nil
|
||||
preferences = {}
|
||||
twitter_preferences = {}
|
||||
if tweet.class == Twitter::DirectMessage
|
||||
article_type = 'twitter direct-message'
|
||||
to = "@#{tweet.recipient.screen_name}"
|
||||
from = "@#{tweet.sender.screen_name}"
|
||||
preferences = {
|
||||
twitter_preferences = {
|
||||
created_at: tweet.created_at,
|
||||
recipient_id: tweet.recipient.id,
|
||||
recipient_screen_name: tweet.recipient.screen_name,
|
||||
@@ -182,7 +182,7 @@ class TweetBase
|
||||
end
|
||||
in_reply_to = tweet.in_reply_to_status_id
|
||||
|
||||
preferences = {
|
||||
twitter_preferences = {
|
||||
mention_ids: mention_ids,
|
||||
geo: tweet.geo,
|
||||
retweeted: tweet.retweeted?,
|
||||
@@ -208,6 +208,17 @@ class TweetBase
|
||||
ticket.save!
|
||||
end
|
||||
|
||||
article_preferences = {
|
||||
twitter: twitter_preferences,
|
||||
links: [
|
||||
{
|
||||
url: "https://twitter.com/statuses/#{tweet.id}",
|
||||
target: '_blank',
|
||||
name: 'on Twitter',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
Ticket::Article.create!(
|
||||
from: from,
|
||||
to: to,
|
||||
@@ -218,16 +229,7 @@ class TweetBase
|
||||
type_id: Ticket::Article::Type.find_by(name: article_type).id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
internal: false,
|
||||
preferences: {
|
||||
twitter: preferences_cleanup(preferences),
|
||||
links: [
|
||||
{
|
||||
url: "https://twitter.com/statuses/#{tweet.id}",
|
||||
target: '_blank',
|
||||
name: 'on Twitter',
|
||||
},
|
||||
],
|
||||
}
|
||||
preferences: preferences_cleanup(article_preferences),
|
||||
)
|
||||
end
|
||||
|
||||
@@ -369,7 +371,7 @@ class TweetBase
|
||||
|
||||
# replace Twitter::NullObject with nill to prevent elasticsearch index issue
|
||||
preferences.each { |_key, value|
|
||||
next if value.class != ActiveSupport::HashWithIndifferentAccess && value.class != Hash
|
||||
next if !value.is_a?(Hash)
|
||||
value.each { |sub_key, sub_level|
|
||||
if sub_level.class == NilClass
|
||||
value[sub_key] = nil
|
||||
|
||||
@@ -1750,4 +1750,696 @@ test("check textLengthWithUrl format", function() {
|
||||
|
||||
});
|
||||
|
||||
// check getRecipientArticle format
|
||||
test('check getRecipientArticle format', function() {
|
||||
|
||||
var customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
var ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
var article = {
|
||||
type: {
|
||||
name: 'phone',
|
||||
},
|
||||
sender: {
|
||||
name: 'Customer',
|
||||
},
|
||||
from: customer.email,
|
||||
to: 'some group',
|
||||
message_id: 'message_id1',
|
||||
created_by: {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'article_created_by@example.com',
|
||||
},
|
||||
}
|
||||
var result = {
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id1',
|
||||
}
|
||||
var verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
type: {
|
||||
name: 'phone',
|
||||
},
|
||||
sender: {
|
||||
name: 'Customer',
|
||||
},
|
||||
from: customer.email,
|
||||
message_id: 'message_id2',
|
||||
created_by: {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'article_created_by@example.com',
|
||||
},
|
||||
}
|
||||
result = {
|
||||
to: '',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id2',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id3',
|
||||
type: {
|
||||
name: 'phone',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
created_by: {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'article_created_by@example.com',
|
||||
},
|
||||
}
|
||||
result = {
|
||||
to: '',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id3',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id4',
|
||||
created_by: customer,
|
||||
type: {
|
||||
name: 'web',
|
||||
},
|
||||
sender: {
|
||||
name: 'Customer',
|
||||
},
|
||||
from: customer.email,
|
||||
to: 'some group',
|
||||
created_by: {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'article_created_by@example.com',
|
||||
},
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id4',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id5',
|
||||
type: {
|
||||
name: 'web',
|
||||
},
|
||||
sender: {
|
||||
name: 'Customer',
|
||||
},
|
||||
from: customer.email,
|
||||
created_by: {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'article_created_by@example.com',
|
||||
}
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id5',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id6',
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Customer',
|
||||
},
|
||||
from: customer.email,
|
||||
to: 'some group',
|
||||
created_by: {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'article_created_by@example.com',
|
||||
}
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id6',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'agent@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id7',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'zammad@example.com',
|
||||
to: 'customer@example.com',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id7',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'agent@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id8',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'agent@example.com',
|
||||
to: 'customer@example.com',
|
||||
}
|
||||
result = {
|
||||
to: 'agent@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id8',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'agent@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id9',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'Agent@Example.com',
|
||||
to: 'customer@example.com',
|
||||
cc: 'zammad@example.com',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id9',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'agent@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id10',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'Agent@Example.com',
|
||||
to: 'customer@example.com, agent@example.com',
|
||||
cc: 'zammad@example.com',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com, agent@example.com',
|
||||
cc: 'zammad@example.com',
|
||||
body: '',
|
||||
in_reply_to: 'message_id10',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, article.created_by, article.type, [], true)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'agent@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id11',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'Agent@Example.com',
|
||||
to: 'customeR@EXAMPLE.com, agent@example.com',
|
||||
cc: 'zammad@example.com, customer@example.com',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com, agent@example.com',
|
||||
cc: 'zammad@example.com',
|
||||
body: '',
|
||||
in_reply_to: 'message_id11',
|
||||
}
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, agent, article.type, [], true)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'agent@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id12',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'Agent@Example.com',
|
||||
to: 'customeR@EXAMPLE.com, agent@example.com, zammad2@EXAMPLE.com',
|
||||
cc: 'zammad@example.com, customer2@example.com',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com, agent@example.com',
|
||||
cc: 'customer2@example.com',
|
||||
body: '',
|
||||
in_reply_to: 'message_id12',
|
||||
}
|
||||
email_addresses = [
|
||||
{
|
||||
email: 'zammad@example.com',
|
||||
},
|
||||
{
|
||||
email: 'zammad2@example.com',
|
||||
}
|
||||
]
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, agent, article.type, email_addresses, true)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'AGENT@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id13',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'Agent@Example.com',
|
||||
to: 'customeR@EXAMPLE.com, agent@example.com, zammad2@EXAMPLE.com',
|
||||
cc: 'zammad@example.com, customer2@example.com',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com, agent@example.com',
|
||||
cc: 'customer2@example.com',
|
||||
body: '',
|
||||
in_reply_to: 'message_id13',
|
||||
}
|
||||
email_addresses = [
|
||||
{
|
||||
email: 'zammad@example.com',
|
||||
},
|
||||
{
|
||||
email: 'zammad2@example.com',
|
||||
}
|
||||
]
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, agent, article.type, email_addresses, true)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'zammad@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id14',
|
||||
created_by: customer,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'zammad@EXAMPLE.com',
|
||||
to: 'customeR@EXAMPLE.com, agent@example.com, zammad2@EXAMPLE.com',
|
||||
cc: 'zammad@example.com, customer2@example.com',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com, agent@example.com',
|
||||
cc: 'customer2@example.com',
|
||||
body: '',
|
||||
in_reply_to: 'message_id14',
|
||||
}
|
||||
email_addresses = [
|
||||
{
|
||||
email: 'zammad@example.com',
|
||||
},
|
||||
{
|
||||
email: 'zammad2@example.com',
|
||||
}
|
||||
]
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, agent, article.type, email_addresses, true)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id15',
|
||||
created_by: customer,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'customer@example.com',
|
||||
to: 'customer1@example.com, customer2@example.com, zammad@example.com',
|
||||
cc: '',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com, customer1@example.com, customer2@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id15',
|
||||
}
|
||||
email_addresses = [
|
||||
{
|
||||
email: 'zammad@example.com',
|
||||
},
|
||||
{
|
||||
email: 'zammad2@example.com',
|
||||
}
|
||||
]
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, agent, article.type, email_addresses, true)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id16',
|
||||
created_by: customer,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'customer@example.com',
|
||||
to: 'customer1@example.com, customer2@example.com, zammad@example.com, customer2+2@example.com',
|
||||
cc: '',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com, customer1@example.com, customer2@example.com, customer2+2@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id16',
|
||||
}
|
||||
email_addresses = [
|
||||
{
|
||||
email: 'zammad@example.com',
|
||||
},
|
||||
{
|
||||
email: 'zammad2@example.com',
|
||||
}
|
||||
]
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, agent, article.type, email_addresses, true)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'zammad@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id17',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'zammad@example.com',
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id17',
|
||||
}
|
||||
email_addresses = [
|
||||
{
|
||||
email: 'zammad@example.com',
|
||||
},
|
||||
{
|
||||
email: 'zammad2@example.com',
|
||||
}
|
||||
]
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, agent, article.type, email_addresses, true)
|
||||
deepEqual(verify, result)
|
||||
|
||||
customer = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'customer@example.com',
|
||||
}
|
||||
agent = {
|
||||
login: 'login',
|
||||
firstname: 'firstname',
|
||||
lastname: 'lastname',
|
||||
email: 'zammad@example.com',
|
||||
}
|
||||
ticket = {
|
||||
customer: customer,
|
||||
}
|
||||
article = {
|
||||
message_id: 'message_id18',
|
||||
created_by: agent,
|
||||
type: {
|
||||
name: 'email',
|
||||
},
|
||||
sender: {
|
||||
name: 'Agent',
|
||||
},
|
||||
from: 'Sender <zammad@example.com>',
|
||||
to: 'Customer <customer@example.com>',
|
||||
cc: '',
|
||||
}
|
||||
result = {
|
||||
to: 'customer@example.com',
|
||||
cc: '',
|
||||
body: '',
|
||||
in_reply_to: 'message_id18',
|
||||
}
|
||||
email_addresses = [
|
||||
{
|
||||
email: 'zammad@example.com',
|
||||
},
|
||||
{
|
||||
email: 'zammad2@example.com',
|
||||
}
|
||||
]
|
||||
verify = App.Utils.getRecipientArticle(ticket, article, agent, article.type, email_addresses, false)
|
||||
deepEqual(verify, result)
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
112
test/controllers/taskbars_controller_test.rb
Normal file
112
test/controllers/taskbars_controller_test.rb
Normal file
@@ -0,0 +1,112 @@
|
||||
# encoding: utf-8
|
||||
require 'test_helper'
|
||||
|
||||
class TaskbarsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
|
||||
# set accept header
|
||||
@headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
|
||||
UserInfo.current_user_id = 1
|
||||
|
||||
# create agent
|
||||
roles = Role.where(name: 'Agent')
|
||||
groups = Group.all
|
||||
|
||||
@agent = User.create_or_update(
|
||||
login: 'taskbar-agent@example.com',
|
||||
firstname: 'Taskbar',
|
||||
lastname: 'Agent',
|
||||
email: 'taskbar-agent@example.com',
|
||||
password: 'agentpw',
|
||||
active: true,
|
||||
roles: roles,
|
||||
groups: groups,
|
||||
)
|
||||
|
||||
# create customer without org
|
||||
roles = Role.where(name: 'Customer')
|
||||
@customer_without_org = User.create_or_update(
|
||||
login: 'taskbar-customer1@example.com',
|
||||
firstname: 'Taskbar',
|
||||
lastname: 'Customer1',
|
||||
email: 'taskbar-customer1@example.com',
|
||||
password: 'customer1pw',
|
||||
active: true,
|
||||
roles: roles,
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
test 'task ownership' do
|
||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('taskbar-agent@example.com', 'agentpw')
|
||||
params = {
|
||||
user_id: @customer_without_org.id,
|
||||
client_id: '123',
|
||||
key: 'Ticket-5',
|
||||
callback: 'TicketZoom',
|
||||
state: {
|
||||
ticket: {
|
||||
owner_id: @agent.id,
|
||||
},
|
||||
article: {},
|
||||
},
|
||||
params: {
|
||||
ticket_id: 5,
|
||||
shown: true,
|
||||
},
|
||||
prio: 3,
|
||||
notify: false,
|
||||
active: false,
|
||||
}
|
||||
|
||||
post '/api/v1/taskbar', params.to_json, @headers.merge('Authorization' => credentials)
|
||||
assert_response(201)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
assert_equal('123', result['client_id'])
|
||||
assert_equal(@agent.id, result['user_id'])
|
||||
assert_equal(5, result['params']['ticket_id'])
|
||||
assert_equal(true, result['params']['shown'])
|
||||
|
||||
taskbar_id = result['id']
|
||||
params[:user_id] = @customer_without_org.id
|
||||
params[:params] = {
|
||||
ticket_id: 5,
|
||||
shown: false,
|
||||
}
|
||||
put "/api/v1/taskbar/#{taskbar_id}", params.to_json, @headers.merge('Authorization' => credentials)
|
||||
assert_response(200)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
assert_equal('123', result['client_id'])
|
||||
assert_equal(@agent.id, result['user_id'])
|
||||
assert_equal(5, result['params']['ticket_id'])
|
||||
assert_equal(false, result['params']['shown'])
|
||||
|
||||
# try to access with other user
|
||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('taskbar-customer1@example.com', 'customer1pw')
|
||||
params = {
|
||||
active: true,
|
||||
}
|
||||
put "/api/v1/taskbar/#{taskbar_id}", params.to_json, @headers.merge('Authorization' => credentials)
|
||||
assert_response(422)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
assert_equal('Not allowed to access this task.', result['error'])
|
||||
|
||||
delete "/api/v1/taskbar/#{taskbar_id}", {}, @headers.merge('Authorization' => credentials)
|
||||
assert_response(422)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
assert_equal('Not allowed to access this task.', result['error'])
|
||||
|
||||
# delete with correct user
|
||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('taskbar-agent@example.com', 'agentpw')
|
||||
delete "/api/v1/taskbar/#{taskbar_id}", {}, @headers.merge('Authorization' => credentials)
|
||||
assert_response(200)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
assert(result.blank?)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,5 +1,6 @@
|
||||
# encoding: utf-8
|
||||
require 'test_helper'
|
||||
require 'rake'
|
||||
|
||||
class UserOrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@@ -83,6 +84,39 @@ class UserOrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||
organization_id: @organization.id,
|
||||
)
|
||||
|
||||
# configure es
|
||||
if ENV['ES_URL'].present?
|
||||
#fail "ERROR: Need ES_URL - hint ES_URL='http://127.0.0.1:9200'"
|
||||
Setting.set('es_url', ENV['ES_URL'])
|
||||
|
||||
# Setting.set('es_url', 'http://127.0.0.1:9200')
|
||||
# Setting.set('es_index', 'estest.local_zammad')
|
||||
# Setting.set('es_user', 'elasticsearch')
|
||||
# Setting.set('es_password', 'zammad')
|
||||
|
||||
if ENV['ES_INDEX_RAND'].present?
|
||||
ENV['ES_INDEX'] = "es_index_#{rand(999_999_999)}"
|
||||
end
|
||||
if ENV['ES_INDEX'].blank?
|
||||
raise "ERROR: Need ES_INDEX - hint ES_INDEX='estest.local_zammad'"
|
||||
end
|
||||
Setting.set('es_index', ENV['ES_INDEX'])
|
||||
|
||||
travel 1.minute
|
||||
|
||||
# drop/create indexes
|
||||
Rake::Task.clear
|
||||
Zammad::Application.load_tasks
|
||||
#Rake::Task["searchindex:drop"].execute
|
||||
#Rake::Task["searchindex:create"].execute
|
||||
Rake::Task['searchindex:rebuild'].execute
|
||||
|
||||
# execute background jobs
|
||||
Scheduler.worker(true)
|
||||
|
||||
sleep 6
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
test 'user create tests - no user' do
|
||||
@@ -460,10 +494,12 @@ class UserOrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
# search as agent
|
||||
Scheduler.worker(true)
|
||||
sleep 2 # let es time to come ready
|
||||
get "/api/v1/users/search?query=#{CGI.escape("Customer#{firstname}")}", {}, @headers.merge('Authorization' => credentials)
|
||||
assert_response(200)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Array, result.class)
|
||||
|
||||
assert_equal(result_user1['id'], result[0]['id'])
|
||||
assert_equal("Customer#{firstname}", result[0]['firstname'])
|
||||
assert_equal('Customer Last', result[0]['lastname'])
|
||||
@@ -489,6 +525,42 @@ class UserOrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_equal("Customer#{firstname} Customer Last <new_customer_by_agent@example.com>", result[0]['value'])
|
||||
assert_not(result[0]['role_ids'])
|
||||
assert_not(result[0]['roles'])
|
||||
|
||||
role = Role.find_by(name: 'Agent')
|
||||
get "/api/v1/users/search?query=#{CGI.escape("Customer#{firstname}")}&role_ids=#{role.id}&label=true", {}, @headers.merge('Authorization' => credentials)
|
||||
assert_response(200)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Array, result.class)
|
||||
assert_equal(0, result.count)
|
||||
|
||||
role = Role.find_by(name: 'Customer')
|
||||
get "/api/v1/users/search?query=#{CGI.escape("Customer#{firstname}")}&role_ids=#{role.id}&label=true", {}, @headers.merge('Authorization' => credentials)
|
||||
assert_response(200)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Array, result.class)
|
||||
assert_equal(result_user1['id'], result[0]['id'])
|
||||
assert_equal("Customer#{firstname} Customer Last <new_customer_by_agent@example.com>", result[0]['label'])
|
||||
assert_equal("Customer#{firstname} Customer Last <new_customer_by_agent@example.com>", result[0]['value'])
|
||||
assert_not(result[0]['role_ids'])
|
||||
assert_not(result[0]['roles'])
|
||||
|
||||
permission = Permission.find_by(name: 'ticket.agent')
|
||||
get "/api/v1/users/search?query=#{CGI.escape("Customer#{firstname}")}&permissions=#{permission.name}&label=true", {}, @headers.merge('Authorization' => credentials)
|
||||
assert_response(200)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Array, result.class)
|
||||
assert_equal(0, result.count)
|
||||
|
||||
permission = Permission.find_by(name: 'ticket.customer')
|
||||
get "/api/v1/users/search?query=#{CGI.escape("Customer#{firstname}")}&permissions=#{permission.name}&label=true", {}, @headers.merge('Authorization' => credentials)
|
||||
assert_response(200)
|
||||
result = JSON.parse(@response.body)
|
||||
assert_equal(Array, result.class)
|
||||
assert_equal(result_user1['id'], result[0]['id'])
|
||||
assert_equal("Customer#{firstname} Customer Last <new_customer_by_agent@example.com>", result[0]['label'])
|
||||
assert_equal("Customer#{firstname} Customer Last <new_customer_by_agent@example.com>", result[0]['value'])
|
||||
assert_not(result[0]['role_ids'])
|
||||
assert_not(result[0]['roles'])
|
||||
end
|
||||
|
||||
test 'user index and create with customer1' do
|
||||
|
||||
@@ -124,6 +124,13 @@ class ElasticsearchTest < ActiveSupport::TestCase
|
||||
assert_not(attributes['password'])
|
||||
assert_not(attributes['organization'])
|
||||
|
||||
attributes = @customer1.search_index_attribute_lookup
|
||||
assert_equal('ES', attributes['firstname'])
|
||||
assert_equal('Customer1', attributes['lastname'])
|
||||
assert_equal('es-customer1@example.com', attributes['email'])
|
||||
assert_not(attributes['password'])
|
||||
assert_equal('Customer Organization Update', attributes['organization'])
|
||||
|
||||
# organization
|
||||
attributes = @organization1.search_index_data
|
||||
assert_equal('Customer Organization Update', attributes['name'])
|
||||
|
||||
@@ -209,7 +209,7 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
}
|
||||
)
|
||||
assert_equal('invalid', result[:result])
|
||||
assert_equal('Authentication failed, username incorrect!', result[:message_human])
|
||||
assert_match(/Authentication failed, username incorrect|Authentication failed, invalid credentials/, result[:message_human])
|
||||
assert_equal('imap.gmail.com', result[:settings][:options][:host])
|
||||
|
||||
result = EmailHelper::Probe.inbound(
|
||||
@@ -225,8 +225,8 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
assert_equal('invalid', result[:result])
|
||||
|
||||
# if we have to many failed logins, we need to handle another error message
|
||||
if result[:message_human] && !result[:message_human].empty?
|
||||
assert_equal('Authentication failed, invalid credentials!', result[:message_human])
|
||||
if result[:message_human].present?
|
||||
assert_match(/Authentication failed, username incorrect|Authentication failed, invalid credentials/, result[:message_human])
|
||||
else
|
||||
assert_match(/Web login required/, result[:message])
|
||||
end
|
||||
@@ -457,7 +457,7 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
email: mailbox_user,
|
||||
password: mailbox_password,
|
||||
)
|
||||
assert_equal(nil, result[:reason])
|
||||
assert_nil(result[:reason])
|
||||
assert_equal('ok', result[:result])
|
||||
assert_equal('pop.gmail.com', result[:setting][:inbound][:options][:host])
|
||||
assert_equal('smtp.gmail.com', result[:setting][:outbound][:options][:host])
|
||||
|
||||
@@ -1,26 +1,52 @@
|
||||
# encoding: utf-8
|
||||
require 'integration_test_helper'
|
||||
require 'webmock/minitest'
|
||||
|
||||
class GeoLocationTest < ActiveSupport::TestCase
|
||||
|
||||
setup do
|
||||
@mock = true
|
||||
#WebMock.allow_net_connect!
|
||||
end
|
||||
|
||||
# check
|
||||
test 'check simple results' do
|
||||
|
||||
if @mock
|
||||
stub_request(:get, 'http://maps.googleapis.com/maps/api/geocode/json?address=Marienstrasse%2013,%2010117%20Berlin&sensor=true')
|
||||
.to_return(status: 200, body: '{"results":[{"geometry":{"location":{"lat": 52.5219143, "lng": 13.3832647}}}]}', headers: {})
|
||||
end
|
||||
|
||||
result = Service::GeoLocation.geocode('Marienstrasse 13, 10117 Berlin')
|
||||
assert(result)
|
||||
assert_equal(52.5219143, result[0])
|
||||
assert_equal(13.3832647, result[1])
|
||||
|
||||
if @mock
|
||||
stub_request(:get, 'http://maps.googleapis.com/maps/api/geocode/json?address=Marienstrasse%2013%2010117%20Berlin&sensor=true')
|
||||
.to_return(status: 200, body: '{"results":[{"geometry":{"location":{"lat": 52.5219143, "lng": 13.3832647}}}]}', headers: {})
|
||||
end
|
||||
|
||||
result = Service::GeoLocation.geocode('Marienstrasse 13 10117 Berlin')
|
||||
assert(result)
|
||||
assert_equal(52.5219143, result[0])
|
||||
assert_equal(13.3832647, result[1])
|
||||
|
||||
if @mock
|
||||
stub_request(:get, 'http://maps.googleapis.com/maps/api/geocode/json?address=Martinsbruggstrasse%2035,%209016%20St.%20Gallen&sensor=true')
|
||||
.to_return(status: 200, body: '{"results":[{"geometry":{"location":{"lat": 47.4366557, "lng": 9.4098904}}}]}', headers: {})
|
||||
end
|
||||
|
||||
result = Service::GeoLocation.geocode('Martinsbruggstrasse 35, 9016 St. Gallen')
|
||||
assert(result)
|
||||
assert_equal(47.4366557, result[0])
|
||||
assert_equal(9.4098904, result[1])
|
||||
|
||||
if @mock
|
||||
stub_request(:get, 'http://maps.googleapis.com/maps/api/geocode/json?address=Martinsbruggstrasse%2035%209016%20St.%20Gallen&sensor=true')
|
||||
.to_return(status: 200, body: '{"results":[{"geometry":{"location":{"lat": 47.4366557, "lng": 9.4098904}}}]}', headers: {})
|
||||
end
|
||||
|
||||
result = Service::GeoLocation.geocode('Martinsbruggstrasse 35 9016 St. Gallen')
|
||||
assert(result)
|
||||
assert_equal(47.4366557, result[0])
|
||||
@@ -30,6 +56,11 @@ class GeoLocationTest < ActiveSupport::TestCase
|
||||
|
||||
test 'check user results' do
|
||||
|
||||
if @mock
|
||||
stub_request(:get, 'http://maps.googleapis.com/maps/api/geocode/json?address=Marienstrasse%2013%2010117%20Berlin&sensor=true')
|
||||
.to_return(status: 200, body: '{"results":[{"geometry":{"location":{"lat": 52.5219143, "lng": 13.3832647}}}]}', headers: {})
|
||||
end
|
||||
|
||||
user1 = User.create(
|
||||
login: 'some_geo_login1',
|
||||
firstname: 'First',
|
||||
@@ -47,6 +78,11 @@ class GeoLocationTest < ActiveSupport::TestCase
|
||||
assert_equal(52.5219143, user1.preferences['lat'])
|
||||
assert_equal(13.3832647, user1.preferences['lng'])
|
||||
|
||||
if @mock
|
||||
stub_request(:get, 'http://maps.googleapis.com/maps/api/geocode/json?address=Marienstrasse%2013,%2010117,%20Berlin&sensor=true')
|
||||
.to_return(status: 200, body: '{"results":[{"geometry":{"location":{"lat": 52.5219143, "lng": 13.3832647}}}]}', headers: {})
|
||||
end
|
||||
|
||||
user2 = User.create(
|
||||
login: 'some_geo_login2',
|
||||
firstname: 'First',
|
||||
@@ -66,6 +102,11 @@ class GeoLocationTest < ActiveSupport::TestCase
|
||||
assert_equal(52.5219143, user2.preferences['lat'])
|
||||
assert_equal(13.3832647, user2.preferences['lng'])
|
||||
|
||||
if @mock
|
||||
stub_request(:get, 'http://maps.googleapis.com/maps/api/geocode/json?address=Martinsbruggstrasse%2035,%209016%20St.%20Gallen&sensor=true')
|
||||
.to_return(status: 200, body: '{"results":[{"geometry":{"location":{"lat": 47.4366557, "lng": 9.4098904}}}]}', headers: {})
|
||||
end
|
||||
|
||||
user3 = User.create(
|
||||
login: 'some_geo_login3',
|
||||
firstname: 'First',
|
||||
@@ -83,6 +124,11 @@ class GeoLocationTest < ActiveSupport::TestCase
|
||||
assert_equal(47.4366557, user3.preferences['lat'])
|
||||
assert_equal(9.4098904, user3.preferences['lng'])
|
||||
|
||||
if @mock
|
||||
stub_request(:get, 'http://maps.googleapis.com/maps/api/geocode/json?address=Martinsbruggstrasse%2035,%209016,%20St.%20Gallen&sensor=true')
|
||||
.to_return(status: 200, body: '{"results":[{"geometry":{"location":{"lat": 47.4366557, "lng": 9.4098904}}}]}', headers: {})
|
||||
end
|
||||
|
||||
user4 = User.create(
|
||||
login: 'some_geo_login4',
|
||||
firstname: 'First',
|
||||
|
||||
@@ -46,7 +46,7 @@ Some Text"
|
||||
article = Ticket::Article.find(article_p.id)
|
||||
|
||||
assert_equal(subject, ticket.title)
|
||||
assert_equal('open', ticket.state.name)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('me+is+customer@example.com', ticket.customer.email)
|
||||
@@ -152,7 +152,7 @@ Some Text"
|
||||
ticket = Ticket.find(ticket_p.id)
|
||||
article = Ticket::Article.find(article_p.id)
|
||||
assert_equal('some subject #2', ticket.title)
|
||||
assert_equal('open', ticket.state.name)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('ticket-system-sender-customer1@example.com', ticket.customer.email)
|
||||
@@ -169,7 +169,7 @@ Some Text"
|
||||
ticket = Ticket.find(ticket_p.id)
|
||||
article = Ticket::Article.find(article_p.id)
|
||||
assert_equal('some subject #3', ticket.title)
|
||||
assert_equal('open', ticket.state.name)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('ticket-system-sender-customer1@example.com', ticket.customer.email)
|
||||
@@ -186,7 +186,7 @@ Some Text"
|
||||
ticket = Ticket.find(ticket_p.id)
|
||||
article = Ticket::Article.find(article_p.id)
|
||||
assert_equal('some subject #4', ticket.title)
|
||||
assert_equal('open', ticket.state.name)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('ticket-system-sender-customer1@example.com', ticket.customer.email)
|
||||
@@ -203,7 +203,7 @@ Some Text"
|
||||
ticket = Ticket.find(ticket_p.id)
|
||||
article = Ticket::Article.find(article_p.id)
|
||||
assert_equal('some subject #5', ticket.title)
|
||||
assert_equal('open', ticket.state.name)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('ticket-system-sender-agent1@example.com', ticket.customer.email)
|
||||
@@ -220,7 +220,7 @@ Some Text"
|
||||
ticket = Ticket.find(ticket_p.id)
|
||||
article = Ticket::Article.find(article_p.id)
|
||||
assert_equal('some subject #6', ticket.title)
|
||||
assert_equal('open', ticket.state.name)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('ticket-system-sender-agent1@example.com', ticket.customer.email)
|
||||
|
||||
152
test/unit/email_process_state_open_set.rb
Normal file
152
test/unit/email_process_state_open_set.rb
Normal file
@@ -0,0 +1,152 @@
|
||||
# encoding: utf-8
|
||||
require 'test_helper'
|
||||
|
||||
class EmailProcessStateOpenSet < ActiveSupport::TestCase
|
||||
|
||||
setup do
|
||||
groups = Group.all
|
||||
roles = Role.where(name: 'Agent')
|
||||
@agent1 = User.create_or_update(
|
||||
login: 'agent-ticket-state-open-set@example.com',
|
||||
firstname: 'StateOpenSet',
|
||||
lastname: 'Agent1',
|
||||
email: 'agent-ticket-state-open-set@example.com',
|
||||
password: 'agentpw',
|
||||
active: true,
|
||||
roles: roles,
|
||||
groups: groups,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
roles = Role.where(name: 'Customer')
|
||||
@customer1 = User.create_or_update(
|
||||
login: 'customer-ticket-state-open-set@example.com',
|
||||
firstname: 'StateOpenSet',
|
||||
lastname: 'Customer',
|
||||
email: 'customer-ticket-state-open-set@example.com',
|
||||
password: 'customerpw',
|
||||
active: true,
|
||||
roles: roles,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
end
|
||||
|
||||
test 'new as agent' do
|
||||
email_raw_string = "From: agent-ticket-state-open-set@example.com
|
||||
To: customer-ticket-state-open-set@example.com
|
||||
Subject: test sender is agent
|
||||
|
||||
Some Text"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
ticket = Ticket.find(ticket_p.id)
|
||||
|
||||
article = Ticket::Article.find(article_p.id)
|
||||
assert_equal('test sender is agent', ticket.title)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('customer-ticket-state-open-set@example.com', ticket.customer.email)
|
||||
assert_equal('StateOpenSet', ticket.customer.firstname)
|
||||
assert_equal('Customer', ticket.customer.lastname)
|
||||
|
||||
article = Ticket::Article.create!(
|
||||
ticket_id: ticket.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'email').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
body: 'test',
|
||||
internal: false,
|
||||
updated_by_id: @customer1.id,
|
||||
created_by_id: @customer1.id,
|
||||
)
|
||||
|
||||
ticket.reload
|
||||
assert_equal('test sender is agent', ticket.title)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Customer', article.sender.name)
|
||||
assert_equal('customer-ticket-state-open-set@example.com', ticket.customer.email)
|
||||
assert_equal('StateOpenSet', ticket.customer.firstname)
|
||||
assert_equal('Customer', ticket.customer.lastname)
|
||||
|
||||
article = Ticket::Article.create!(
|
||||
ticket_id: ticket.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'email').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Agent').id,
|
||||
body: 'test',
|
||||
internal: false,
|
||||
updated_by_id: @agent1.id,
|
||||
created_by_id: @agent1.id,
|
||||
)
|
||||
|
||||
ticket.reload
|
||||
assert_equal('test sender is agent', ticket.title)
|
||||
assert_equal('open', ticket.state.name)
|
||||
assert_equal('Agent', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('customer-ticket-state-open-set@example.com', ticket.customer.email)
|
||||
assert_equal('StateOpenSet', ticket.customer.firstname)
|
||||
assert_equal('Customer', ticket.customer.lastname)
|
||||
|
||||
end
|
||||
|
||||
test 'new as customer' do
|
||||
email_raw_string = "From: customer-ticket-state-open-set@example.com
|
||||
To: agent-ticket-state-open-set@example.com
|
||||
Subject: test sender is customer
|
||||
|
||||
Some Text"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
ticket = Ticket.find(ticket_p.id)
|
||||
|
||||
article = Ticket::Article.find(article_p.id)
|
||||
assert_equal('test sender is customer', ticket.title)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Customer', ticket.create_article_sender.name)
|
||||
assert_equal('Customer', article.sender.name)
|
||||
assert_equal('customer-ticket-state-open-set@example.com', ticket.customer.email)
|
||||
assert_equal('StateOpenSet', ticket.customer.firstname)
|
||||
assert_equal('Customer', ticket.customer.lastname)
|
||||
|
||||
article = Ticket::Article.create!(
|
||||
ticket_id: ticket.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'email').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
body: 'test',
|
||||
internal: false,
|
||||
updated_by_id: @customer1.id,
|
||||
created_by_id: @customer1.id,
|
||||
)
|
||||
|
||||
ticket.reload
|
||||
assert_equal('test sender is customer', ticket.title)
|
||||
assert_equal('new', ticket.state.name)
|
||||
assert_equal('Customer', ticket.create_article_sender.name)
|
||||
assert_equal('Customer', article.sender.name)
|
||||
assert_equal('customer-ticket-state-open-set@example.com', ticket.customer.email)
|
||||
assert_equal('StateOpenSet', ticket.customer.firstname)
|
||||
assert_equal('Customer', ticket.customer.lastname)
|
||||
|
||||
article = Ticket::Article.create!(
|
||||
ticket_id: ticket.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'email').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Agent').id,
|
||||
body: 'test',
|
||||
internal: false,
|
||||
updated_by_id: @agent1.id,
|
||||
created_by_id: @agent1.id,
|
||||
)
|
||||
|
||||
ticket.reload
|
||||
assert_equal('test sender is customer', ticket.title)
|
||||
assert_equal('open', ticket.state.name)
|
||||
assert_equal('Customer', ticket.create_article_sender.name)
|
||||
assert_equal('Agent', article.sender.name)
|
||||
assert_equal('customer-ticket-state-open-set@example.com', ticket.customer.email)
|
||||
assert_equal('StateOpenSet', ticket.customer.firstname)
|
||||
assert_equal('Customer', ticket.customer.lastname)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -8,40 +8,12 @@ class IntegrationIcingaTest < ActiveSupport::TestCase
|
||||
# http://docs.icinga.org/icinga2/latest/doc/module/icinga2/chapter/monitoring-basics#host-states
|
||||
# http://docs.icinga.org/icinga2/latest/doc/module/icinga2/chapter/monitoring-basics#service-states
|
||||
|
||||
test 'base tests' do
|
||||
|
||||
setup do
|
||||
Setting.set('icinga_integration', true)
|
||||
Setting.set('icinga_sender', 'icinga2@monitoring.example.com')
|
||||
end
|
||||
|
||||
# not matching sender
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: PROBLEM - host.internal.loc - CPU Load is WARNING
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-1-0@monitoring.znuny.com>
|
||||
From: icinga_not_matching@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address:=20
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info: WARNING - load average: 3.44, 0.99, 0.35
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_p.state.name)
|
||||
assert(ticket_p.preferences)
|
||||
assert_not(ticket_p.preferences['icinga'])
|
||||
test 'base tests' do
|
||||
|
||||
# RBL check
|
||||
email_raw_string = "To: support@example.com
|
||||
@@ -51,7 +23,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-1-1@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga 2 Service Monitoring on apn4711.dc.example.com *****
|
||||
|
||||
@@ -82,7 +54,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-1-2@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga 2 Service Monitoring on apn4711.dc.example.com *****
|
||||
|
||||
@@ -113,7 +85,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-1-2@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga 2 Service Monitoring on apn4711.dc.example.com *****
|
||||
|
||||
@@ -145,7 +117,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-2@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
@@ -179,7 +151,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-3@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
@@ -214,7 +186,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-4@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
@@ -249,7 +221,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-5@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
@@ -284,7 +256,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-5@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
@@ -317,7 +289,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-5@monitoring.znuny.com>
|
||||
From: icinga@monitoring.example.com (icinga)
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
@@ -342,8 +314,295 @@ Comment: [] =
|
||||
assert_nil(ticket_3_1.preferences['icinga']['service'])
|
||||
assert_equal('DOWN', ticket_3_1.preferences['icinga']['state'])
|
||||
|
||||
#Setting.set('icinga_integration', false)
|
||||
# ping down
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: [PROBLEM] Ping IPv4 on apn4711.dc.example.com is WARNING!
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Service Monitoring on monitoring.znuny.com *****
|
||||
|
||||
Ping IPv4 on apn4711.dc.example.com is WARNING!
|
||||
|
||||
Info: PING WARNING - Packet loss =3D 0%, RTA =3D 160.57 ms
|
||||
|
||||
When: 2017-09-28 09:41:03 +0200
|
||||
Service: Ping IPv4
|
||||
Host: apn4711.dc.example.com
|
||||
IPv4: 127.0.0.1="
|
||||
|
||||
ticket_4, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_4.state.name)
|
||||
assert(ticket_4.preferences)
|
||||
assert(ticket_4.preferences['icinga'])
|
||||
assert_equal('apn4711.dc.example.com', ticket_4.preferences['icinga']['host'])
|
||||
assert_equal('Ping IPv4', ticket_4.preferences['icinga']['service'])
|
||||
assert_equal('WARNING', ticket_4.preferences['icinga']['state'])
|
||||
assert_not_equal(ticket_4.id, ticket_1.id)
|
||||
|
||||
# ping up
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: [RECOVERY] Ping IPv4 on apn4711.dc.example.com is OK!
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Service Monitoring on monitoring.znuny.com *****
|
||||
|
||||
Ping IPv4 on apn4711.dc.example.com is OK!
|
||||
|
||||
Info: PING OK - Packet loss =3D 0%, RTA =3D 20.23 ms
|
||||
|
||||
When: 2017-09-28 11:42:01 +0200
|
||||
Service: Ping IPv4
|
||||
Host: apn4711.dc.example.com
|
||||
IPv4: 127.0.0.1="
|
||||
|
||||
ticket_4_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal(ticket_4.id, ticket_4_1.id)
|
||||
assert_equal('closed', ticket_4_1.state.name)
|
||||
assert(ticket_4_1.preferences)
|
||||
assert(ticket_4_1.preferences['icinga'])
|
||||
assert_equal('apn4711.dc.example.com', ticket_4.preferences['icinga']['host'])
|
||||
assert_equal('Ping IPv4', ticket_4.preferences['icinga']['service'])
|
||||
assert_equal('WARNING', ticket_4_1.preferences['icinga']['state'])
|
||||
end
|
||||
|
||||
test 'not matching sender tests' do
|
||||
|
||||
# not matching sender
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: PROBLEM - host.internal.loc - CPU Load is WARNING
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-1-0@monitoring.znuny.com>
|
||||
From: icinga_not_matching@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address:=20
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info: WARNING - load average: 3.44, 0.99, 0.35
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_p.state.name)
|
||||
assert(ticket_p.preferences)
|
||||
assert_not(ticket_p.preferences['icinga'])
|
||||
|
||||
Setting.set('icinga_sender', 'regex:icinga2@monitoring.example.com')
|
||||
|
||||
# not matching sender
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: PROBLEM - host.internal.loc - CPU Load is WARNING
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-1-0@monitoring.znuny.com>
|
||||
From: icinga_not_matching@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address:=20
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info: WARNING - load average: 3.44, 0.99, 0.35
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_p.state.name)
|
||||
assert(ticket_p.preferences)
|
||||
assert_not(ticket_p.preferences['icinga'])
|
||||
|
||||
# not matching sender
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: PROBLEM - host.internal.loc - CPU Load is WARNING
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-1-0@monitoring.znuny.com>
|
||||
Return-Path: bob@example.com
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address:=20
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info: WARNING - load average: 3.44, 0.99, 0.35
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_p.state.name)
|
||||
assert(ticket_p.preferences)
|
||||
assert_not(ticket_p.preferences['icinga'])
|
||||
end
|
||||
|
||||
test 'matching sender tests' do
|
||||
|
||||
# matching sender - follow up - CPU Load/host.internal.loc
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: PROBLEM - host.internal.loc - CPU Load is WARNING
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-4@monitoring.znuny.com>
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address:=20
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info: WARNING - load average: 3.44, 0.99, 0.35
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
|
||||
ticket_1_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_1_1.state.name)
|
||||
assert(ticket_1_1.preferences)
|
||||
assert(ticket_1_1.preferences['icinga'])
|
||||
assert_equal('host.internal.loc', ticket_1_1.preferences['icinga']['host'])
|
||||
assert_equal('CPU Load', ticket_1_1.preferences['icinga']['service'])
|
||||
assert_equal('WARNING', ticket_1_1.preferences['icinga']['state'])
|
||||
|
||||
Setting.set('icinga_sender', 'regex:icinga2@monitoring.example.com')
|
||||
|
||||
# matching sender I
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: PROBLEM - host1.internal.loc - CPU Load is WARNING
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-4@monitoring.znuny.com>
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host1.internal.loc
|
||||
Address:=20
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info: WARNING - load average: 3.44, 0.99, 0.35
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
|
||||
ticket_1_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_1_1.state.name)
|
||||
assert(ticket_1_1.preferences)
|
||||
assert(ticket_1_1.preferences['icinga'])
|
||||
assert_equal('host1.internal.loc', ticket_1_1.preferences['icinga']['host'])
|
||||
assert_equal('CPU Load', ticket_1_1.preferences['icinga']['service'])
|
||||
assert_equal('WARNING', ticket_1_1.preferences['icinga']['state'])
|
||||
|
||||
# matching sender I
|
||||
Setting.set('icinga_sender', 'regex:(icinga2|abc123)@monitoring.example.com')
|
||||
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: PROBLEM - host2.internal.loc - CPU Load is WARNING
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-4@monitoring.znuny.com>
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host2.internal.loc
|
||||
Address:=20
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info: WARNING - load average: 3.44, 0.99, 0.35
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
|
||||
ticket_1_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_1_1.state.name)
|
||||
assert(ticket_1_1.preferences)
|
||||
assert(ticket_1_1.preferences['icinga'])
|
||||
assert_equal('host2.internal.loc', ticket_1_1.preferences['icinga']['host'])
|
||||
assert_equal('CPU Load', ticket_1_1.preferences['icinga']['service'])
|
||||
assert_equal('WARNING', ticket_1_1.preferences['icinga']['state'])
|
||||
|
||||
end
|
||||
|
||||
test 'recover without problem tests' do
|
||||
|
||||
# host up without problem
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: RECOVERY - apn4711.dc.example.com is UP
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-icinga-5@monitoring.znuny.com>
|
||||
From: icinga2@monitoring.example.com (icinga)
|
||||
|
||||
***** Icinga *****
|
||||
|
||||
Notification Type: RECOVERY
|
||||
|
||||
Host: apn4711.dc.example.com
|
||||
Address: 127.0.0.1
|
||||
State: UP
|
||||
|
||||
Date/Time: 2017-01-14 12:07:11 +0100
|
||||
|
||||
Additional Info: PING OK - Packet loss = 0%, RTA = 21.37 ms
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
ticket_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
ticket_count = Ticket.count
|
||||
assert_not(ticket_1)
|
||||
assert_equal(ticket_count, Ticket.count)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -6,38 +6,12 @@ class IntegrationNagiosTest < ActiveSupport::TestCase
|
||||
# according
|
||||
# https://github.com/NagiosEnterprises/nagioscore/blob/754218e67653929a58938b99ef6b6039b6474fe4/sample-config/template-object/commands.cfg.in#L35
|
||||
|
||||
test 'base tests' do
|
||||
|
||||
setup do
|
||||
Setting.set('nagios_integration', true)
|
||||
Setting.set('nagios_sender', 'nagios2@monitoring.example.com')
|
||||
end
|
||||
|
||||
# not matching sender
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: ** PROBLEM Service Alert: host.internal.loc/CPU Load is WARNING **
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-1@monitoring.znuny.com>
|
||||
From: nagios_not_matching@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address: 1.1.1.1
|
||||
State: PROBLEM
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info:
|
||||
WARNING - load average: 3.44, 0.99, 0.35
|
||||
"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_p.state.name)
|
||||
assert(ticket_p.preferences)
|
||||
assert_not(ticket_p.preferences['nagios'])
|
||||
test 'base tests' do
|
||||
|
||||
# matching sender - CPU Load/host.internal.loc
|
||||
email_raw_string = "To: support@example.com
|
||||
@@ -46,7 +20,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-2@monitoring.znuny.com>
|
||||
From: nagios@monitoring.example.com (nagios)
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
@@ -78,7 +52,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-3@monitoring.znuny.com>
|
||||
From: nagios@monitoring.example.com (nagios)
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
@@ -111,7 +85,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-4@monitoring.znuny.com>
|
||||
From: nagios@monitoring.example.com (nagios)
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
@@ -144,7 +118,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-5@monitoring.znuny.com>
|
||||
From: nagios@monitoring.example.com (nagios)
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
@@ -176,7 +150,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-5@monitoring.znuny.com>
|
||||
From: nagios@monitoring.example.com (nagios)
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
@@ -209,7 +183,7 @@ MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-5@monitoring.znuny.com>
|
||||
From: nagios@monitoring.example.com (nagios)
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
@@ -238,4 +212,232 @@ Comment: [] =
|
||||
|
||||
end
|
||||
|
||||
test 'not matching sender tests' do
|
||||
|
||||
# not matching sender
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: ** PROBLEM Service Alert: host.internal.loc/CPU Load is WARNING **
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-1@monitoring.znuny.com>
|
||||
From: nagios_not_matching@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address: 1.1.1.1
|
||||
State: PROBLEM
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info:
|
||||
WARNING - load average: 3.44, 0.99, 0.35
|
||||
"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_p.state.name)
|
||||
assert(ticket_p.preferences)
|
||||
assert_not(ticket_p.preferences['nagios'])
|
||||
|
||||
Setting.set('nagios_sender', 'regex:icinga2@monitoring.example.com')
|
||||
|
||||
# not matching sender
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: ** PROBLEM Service Alert: host.internal.loc/CPU Load is WARNING **
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-1@monitoring.znuny.com>
|
||||
From: nagios_not_matching@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address: 1.1.1.1
|
||||
State: PROBLEM
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info:
|
||||
WARNING - load average: 3.44, 0.99, 0.35
|
||||
"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_p.state.name)
|
||||
assert(ticket_p.preferences)
|
||||
assert_not(ticket_p.preferences['nagios'])
|
||||
|
||||
# not matching sender
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: ** PROBLEM Service Alert: host.internal.loc/CPU Load is WARNING **
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-1@monitoring.znuny.com>
|
||||
Return-Path: bob@example.com
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address: 1.1.1.1
|
||||
State: PROBLEM
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info:
|
||||
WARNING - load average: 3.44, 0.99, 0.35
|
||||
"
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_p.state.name)
|
||||
assert(ticket_p.preferences)
|
||||
assert_not(ticket_p.preferences['nagios'])
|
||||
end
|
||||
|
||||
test 'matching sender tests' do
|
||||
|
||||
# matching sender - follow up - CPU Load/host.internal.loc
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: ** PROBLEM Service Alert: host.internal.loc/CPU Load is WARNING **
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-2@monitoring.znuny.com>
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host.internal.loc
|
||||
Address: 1.1.1.1
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info:
|
||||
WARNING - load average: 3.44, 0.99, 0.35
|
||||
"
|
||||
|
||||
ticket_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_1.state.name)
|
||||
assert(ticket_1.preferences)
|
||||
assert(ticket_1.preferences['nagios'])
|
||||
assert_equal('host.internal.loc', ticket_1.preferences['nagios']['host'])
|
||||
assert_equal('CPU Load', ticket_1.preferences['nagios']['service'])
|
||||
assert_equal('WARNING', ticket_1.preferences['nagios']['state'])
|
||||
|
||||
Setting.set('icinga_sender', 'regex:icinga2@monitoring.example.com')
|
||||
|
||||
# matching sender I
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: ** PROBLEM Service Alert: host.internal.loc/CPU Load is WARNING **
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-2@monitoring.znuny.com>
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host1.internal.loc
|
||||
Address: 1.1.1.1
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info:
|
||||
WARNING - load average: 3.44, 0.99, 0.35
|
||||
"
|
||||
|
||||
ticket_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_1.state.name)
|
||||
assert(ticket_1.preferences)
|
||||
assert(ticket_1.preferences['nagios'])
|
||||
assert_equal('host1.internal.loc', ticket_1.preferences['nagios']['host'])
|
||||
assert_equal('CPU Load', ticket_1.preferences['nagios']['service'])
|
||||
assert_equal('WARNING', ticket_1.preferences['nagios']['state'])
|
||||
|
||||
# matching sender I
|
||||
Setting.set('icinga_sender', 'regex:(icinga2|abc123)@monitoring.example.com')
|
||||
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: ** PROBLEM Service Alert: host.internal.loc/CPU Load is WARNING **
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-2@monitoring.znuny.com>
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
Notification Type: PROBLEM
|
||||
|
||||
Service: CPU Load
|
||||
Host: host2.internal.loc
|
||||
Address: 1.1.1.1
|
||||
State: WARNING
|
||||
|
||||
Date/Time: 2016-01-31 10:46:20 +0100
|
||||
|
||||
Additional Info:
|
||||
WARNING - load average: 3.44, 0.99, 0.35
|
||||
"
|
||||
|
||||
ticket_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal('new', ticket_1.state.name)
|
||||
assert(ticket_1.preferences)
|
||||
assert(ticket_1.preferences['nagios'])
|
||||
assert_equal('host2.internal.loc', ticket_1.preferences['nagios']['host'])
|
||||
assert_equal('CPU Load', ticket_1.preferences['nagios']['service'])
|
||||
assert_equal('WARNING', ticket_1.preferences['nagios']['state'])
|
||||
|
||||
end
|
||||
|
||||
test 'recover without problem tests' do
|
||||
|
||||
# host up without problem
|
||||
email_raw_string = "To: support@example.com
|
||||
Subject: ** RECOVERY Host Alert: apn4711.dc.example.com is UP **
|
||||
User-Agent: Heirloom mailx 12.5 7/5/10
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <20160131094621.29ECD400F29C-nagios-5@monitoring.znuny.com>
|
||||
From: nagios2@monitoring.example.com (nagios)
|
||||
|
||||
***** Nagios *****
|
||||
|
||||
Notification Type: RECOVERY
|
||||
|
||||
Host: apn4711.dc.example.com
|
||||
Address: 127.0.0.1
|
||||
State: UP
|
||||
|
||||
Date/Time: 2017-01-14 12:07:11 +0100
|
||||
|
||||
Additional Info: PING OK - Packet loss = 0%, RTA = 21.37 ms
|
||||
|
||||
Comment: [] =
|
||||
"
|
||||
ticket_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
ticket_count = Ticket.count
|
||||
assert_not(ticket_1)
|
||||
assert_equal(ticket_count, Ticket.count)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
103
test/unit/ticket_article_dos_test.rb
Normal file
103
test/unit/ticket_article_dos_test.rb
Normal file
@@ -0,0 +1,103 @@
|
||||
# encoding: utf-8
|
||||
require 'test_helper'
|
||||
|
||||
class TicketArticleDos < ActiveSupport::TestCase
|
||||
|
||||
test 'check body size' do
|
||||
|
||||
org_community = Organization.create_if_not_exists(
|
||||
name: 'Zammad Foundation',
|
||||
)
|
||||
user_community = User.create_or_update(
|
||||
login: 'article.dos@example.org',
|
||||
firstname: 'Article',
|
||||
lastname: 'Dos',
|
||||
email: 'article.dos@example.org',
|
||||
password: '',
|
||||
active: true,
|
||||
roles: [ Role.find_by(name: 'Customer') ],
|
||||
organization_id: org_community.id,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
UserInfo.current_user_id = user_community.id
|
||||
ApplicationHandleInfo.current = 'test.postmaster'
|
||||
|
||||
ticket1 = Ticket.create!(
|
||||
group_id: Group.first.id,
|
||||
customer_id: user_community.id,
|
||||
title: 'DoS 1!',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
article1 = Ticket::Article.create!(
|
||||
ticket_id: ticket1.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'phone').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
from: 'Zammad Feedback <feedback@example.org>',
|
||||
body: Array.new(2_000_000) { [*'0'..'9', *'a'..'z', ' ', ' ', ' ', '. '].sample }.join,
|
||||
internal: false,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
assert_equal(1_500_000, article1.body.length)
|
||||
|
||||
ticket2 = Ticket.create!(
|
||||
group_id: Group.first.id,
|
||||
customer_id: user_community.id,
|
||||
title: 'DoS 2!',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
article2 = Ticket::Article.create!(
|
||||
ticket_id: ticket2.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'phone').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
from: 'Zammad Feedback <feedback@example.org>',
|
||||
body: "\u0000#{Array.new(2_000_000) { [*'0'..'9', *'a'..'z', ' ', ' ', ' ', '. '].sample }.join}",
|
||||
internal: false,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
assert_equal(1_500_000, article2.body.length)
|
||||
|
||||
ApplicationHandleInfo.current = 'web'
|
||||
|
||||
ticket3 = Ticket.create!(
|
||||
group_id: Group.first.id,
|
||||
customer_id: user_community.id,
|
||||
title: 'DoS 3!',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
assert_raises(Exceptions::UnprocessableEntity) {
|
||||
article3 = Ticket::Article.create!(
|
||||
ticket_id: ticket3.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'phone').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
from: 'Zammad Feedback <feedback@example.org>',
|
||||
body: "\u0000#{Array.new(2_000_000) { [*'0'..'9', *'a'..'z', ' ', ' ', ' ', '. '].sample }.join}",
|
||||
internal: false,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
test 'check body size / cut if email' do
|
||||
|
||||
email_raw_string = "From: me@example.com
|
||||
To: customer@example.com
|
||||
Subject: some new subject
|
||||
|
||||
Some Text" + Array.new(2_000_000) { [*'0'..'9', *'a'..'z', ' ', ' ', ' ', '. '].sample }.join
|
||||
|
||||
ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string)
|
||||
assert_equal(1_500_000, article_p.body.length)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
69
test/unit/ticket_article_twitter_test.rb
Normal file
69
test/unit/ticket_article_twitter_test.rb
Normal file
@@ -0,0 +1,69 @@
|
||||
# encoding: utf-8
|
||||
require 'test_helper'
|
||||
|
||||
class TicketArticleTwitter < ActiveSupport::TestCase
|
||||
|
||||
test 'preferences cleanup' do
|
||||
|
||||
org_community = Organization.create_if_not_exists(
|
||||
name: 'Zammad Foundation',
|
||||
)
|
||||
user_community = User.create_or_update(
|
||||
login: 'article.twitter@example.org',
|
||||
firstname: 'Article',
|
||||
lastname: 'Twitter',
|
||||
email: 'article.twitter@example.org',
|
||||
password: '',
|
||||
active: true,
|
||||
roles: [ Role.find_by(name: 'Customer') ],
|
||||
organization_id: org_community.id,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
ticket1 = Ticket.create!(
|
||||
group_id: Group.first.id,
|
||||
customer_id: user_community.id,
|
||||
title: 'Tweet 1!',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
article1 = Ticket::Article.create!(
|
||||
ticket_id: ticket1.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'twitter status').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
from: '@example',
|
||||
body: 'some tweet',
|
||||
internal: false,
|
||||
preferences: TweetBase.new.preferences_cleanup(ActiveSupport::HashWithIndifferentAccess.new(
|
||||
twitter: {
|
||||
'mention_ids' => [1_234_567_890],
|
||||
'geo' => Twitter::NullObject.new,
|
||||
'retweeted' => false,
|
||||
'possibly_sensitive' => false,
|
||||
'in_reply_to_user_id' => 1_234_567_890,
|
||||
'place' => Twitter::NullObject.new,
|
||||
'retweet_count' => 0,
|
||||
'source' => '<a href="http://example.com/software/tweetbot/mac" rel="nofollow">Tweetbot for Mac</a>',
|
||||
'favorited' => false,
|
||||
'truncated' => false
|
||||
},
|
||||
links: [
|
||||
{
|
||||
url: 'https://twitter.com/statuses/123',
|
||||
target: '_blank',
|
||||
name: 'on Twitter',
|
||||
},
|
||||
],
|
||||
)),
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
assert(article1.preferences[:twitter])
|
||||
assert_equal(1_234_567_890, article1.preferences[:twitter][:mention_ids][0])
|
||||
assert_nil(article1.preferences[:twitter][:geo])
|
||||
assert_equal(NilClass, article1.preferences[:twitter][:geo].class)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
321
test/unit/ticket_overview_out_of_office_test.rb
Normal file
321
test/unit/ticket_overview_out_of_office_test.rb
Normal file
@@ -0,0 +1,321 @@
|
||||
# encoding: utf-8
|
||||
require 'test_helper'
|
||||
|
||||
class TicketOverviewOutOfOfficeTest < ActiveSupport::TestCase
|
||||
|
||||
setup do
|
||||
group = Group.create_or_update(
|
||||
name: 'OverviewReplacementTest',
|
||||
updated_at: '2015-02-05 16:37:00',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
roles = Role.where(name: 'Agent')
|
||||
@agent1 = User.create_or_update(
|
||||
login: 'ticket-overview-agent1@example.com',
|
||||
firstname: 'Overview',
|
||||
lastname: 'Agent1',
|
||||
email: 'ticket-overview-agent1@example.com',
|
||||
password: 'agentpw',
|
||||
active: true,
|
||||
roles: roles,
|
||||
groups: [group],
|
||||
out_of_office: false,
|
||||
updated_at: '2015-02-05 16:37:00',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
@agent2 = User.create_or_update(
|
||||
login: 'ticket-overview-agent2@example.com',
|
||||
firstname: 'Overview',
|
||||
lastname: 'Agent2',
|
||||
email: 'ticket-overview-agent2@example.com',
|
||||
password: 'agentpw',
|
||||
active: true,
|
||||
roles: roles,
|
||||
groups: [group],
|
||||
out_of_office: false,
|
||||
updated_at: '2015-02-05 16:38:00',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
roles = Role.where(name: 'Customer')
|
||||
organization1 = Organization.create_or_update(
|
||||
name: 'Overview Org',
|
||||
updated_at: '2015-02-05 16:37:00',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
@customer1 = User.create_or_update(
|
||||
login: 'ticket-overview-customer1@example.com',
|
||||
firstname: 'Overview',
|
||||
lastname: 'Customer1',
|
||||
email: 'ticket-overview-customer1@example.com',
|
||||
password: 'customerpw',
|
||||
active: true,
|
||||
organization_id: organization1.id,
|
||||
roles: roles,
|
||||
out_of_office: false,
|
||||
updated_at: '2015-02-05 16:37:00',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
Overview.destroy_all
|
||||
UserInfo.current_user_id = 1
|
||||
overview_role = Role.find_by(name: 'Agent')
|
||||
@overview1 = Overview.create_or_update(
|
||||
name: 'My replacement Tickets',
|
||||
link: 'my_replacement',
|
||||
prio: 1000,
|
||||
role_ids: [overview_role.id],
|
||||
out_of_office: true,
|
||||
condition: {
|
||||
'ticket.state_id' => {
|
||||
operator: 'is',
|
||||
value: Ticket::State.by_category(:open).pluck(:id),
|
||||
},
|
||||
'ticket.out_of_office_replacement_id' => {
|
||||
operator: 'is',
|
||||
pre_condition: 'current_user.id',
|
||||
},
|
||||
},
|
||||
order: {
|
||||
by: 'created_at',
|
||||
direction: 'ASC',
|
||||
},
|
||||
view: {
|
||||
d: %w(title customer group created_at),
|
||||
s: %w(title customer group created_at),
|
||||
m: %w(number title customer group created_at),
|
||||
view_mode_default: 's',
|
||||
},
|
||||
)
|
||||
@overview2 = Overview.create_if_not_exists(
|
||||
name: 'My assigned Tickets',
|
||||
link: 'my_assigned',
|
||||
prio: 900,
|
||||
role_ids: [overview_role.id],
|
||||
condition: {
|
||||
'ticket.state_id' => {
|
||||
operator: 'is',
|
||||
value: Ticket::State.by_category(:open).pluck(:id),
|
||||
},
|
||||
'ticket.owner_id' => {
|
||||
operator: 'is',
|
||||
pre_condition: 'current_user.id',
|
||||
},
|
||||
},
|
||||
order: {
|
||||
by: 'created_at',
|
||||
direction: 'ASC',
|
||||
},
|
||||
view: {
|
||||
d: %w(title customer group created_at),
|
||||
s: %w(title customer group created_at),
|
||||
m: %w(number title customer group created_at),
|
||||
view_mode_default: 's',
|
||||
},
|
||||
)
|
||||
|
||||
overview_role = Role.find_by(name: 'Customer')
|
||||
@overview3 = Overview.create_or_update(
|
||||
name: 'My Tickets',
|
||||
link: 'my_tickets',
|
||||
prio: 1100,
|
||||
role_ids: [overview_role.id],
|
||||
out_of_office: true,
|
||||
condition: {
|
||||
'ticket.state_id' => {
|
||||
operator: 'is',
|
||||
value: [1, 2, 3, 4, 6, 7],
|
||||
},
|
||||
'ticket.out_of_office_replacement_id' => {
|
||||
operator: 'is',
|
||||
pre_condition: 'current_user.organization_id',
|
||||
},
|
||||
},
|
||||
order: {
|
||||
by: 'created_at',
|
||||
direction: 'DESC',
|
||||
},
|
||||
view: {
|
||||
d: %w(title customer state created_at),
|
||||
s: %w(number title state created_at),
|
||||
m: %w(number title state created_at),
|
||||
view_mode_default: 's',
|
||||
},
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
test 'overview index' do
|
||||
result = Ticket::Overviews.all(
|
||||
current_user: @agent1,
|
||||
)
|
||||
assert_equal(1, result.count)
|
||||
assert_equal('My assigned Tickets', result[0].name)
|
||||
|
||||
result = Ticket::Overviews.all(
|
||||
current_user: @agent2,
|
||||
)
|
||||
assert_equal(1, result.count)
|
||||
assert_equal('My assigned Tickets', result[0].name)
|
||||
|
||||
result = Ticket::Overviews.all(
|
||||
current_user: @customer1,
|
||||
)
|
||||
assert_equal(1, result.count)
|
||||
assert_equal('My Tickets', result[0].name)
|
||||
@agent1.out_of_office = true
|
||||
@agent1.out_of_office_start_at = Time.zone.now - 2.days
|
||||
@agent1.out_of_office_end_at = Time.zone.now + 2.days
|
||||
@agent1.out_of_office_replacement_id = @agent2.id
|
||||
@agent1.save!
|
||||
|
||||
result = Ticket::Overviews.all(
|
||||
current_user: @agent1,
|
||||
)
|
||||
assert_equal(1, result.count)
|
||||
assert_equal('My assigned Tickets', result[0].name)
|
||||
|
||||
result = Ticket::Overviews.all(
|
||||
current_user: @agent2,
|
||||
)
|
||||
assert_equal(2, result.count)
|
||||
assert_equal('My assigned Tickets', result[0].name)
|
||||
assert_equal('My replacement Tickets', result[1].name)
|
||||
|
||||
result = Ticket::Overviews.all(
|
||||
current_user: @customer1,
|
||||
)
|
||||
assert_equal(1, result.count)
|
||||
assert_equal('My Tickets', result[0].name)
|
||||
end
|
||||
|
||||
test 'overview shown' do
|
||||
result = Ticket::Overviews.index(@agent1)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].blank?)
|
||||
|
||||
result = Ticket::Overviews.index(@agent2)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].blank?)
|
||||
|
||||
result = Ticket::Overviews.index(@customer1)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_tickets')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].blank?)
|
||||
|
||||
@agent1.out_of_office = true
|
||||
@agent1.out_of_office_start_at = Time.zone.now - 2.days
|
||||
@agent1.out_of_office_end_at = Time.zone.now + 2.days
|
||||
@agent1.out_of_office_replacement_id = @agent2.id
|
||||
@agent1.save!
|
||||
|
||||
p User.where(active: true, out_of_office: true, out_of_office_replacement_id: @agent2.id)
|
||||
p User.where(active: true, out_of_office: true, out_of_office_replacement_id: @agent2.id).where('out_of_office_start_at <= ? AND out_of_office_end_at >= ?', Time.zone.today, Time.zone.today)
|
||||
assert_equal(@agent2.out_of_office_agent_of.count, 1)
|
||||
assert(@agent2.out_of_office_agent_of[0])
|
||||
assert_equal(@agent2.out_of_office_agent_of[0].id, @agent1.id)
|
||||
|
||||
result = Ticket::Overviews.index(@agent1)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].blank?)
|
||||
|
||||
result = Ticket::Overviews.index(@agent2)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert(result[1])
|
||||
assert_equal(result[1][:overview][:name], 'My replacement Tickets')
|
||||
assert_equal(result[1][:overview][:view], 'my_replacement')
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].blank?)
|
||||
|
||||
result = Ticket::Overviews.index(@customer1)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_tickets')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].blank?)
|
||||
|
||||
ticket1 = Ticket.create!(
|
||||
title: 'overview test 1',
|
||||
group: Group.lookup(name: 'OverviewReplacementTest'),
|
||||
customer_id: 2,
|
||||
owner_id: @agent1.id,
|
||||
state: Ticket::State.lookup(name: 'new'),
|
||||
priority: Ticket::Priority.lookup(name: '2 normal'),
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
article1 = Ticket::Article.create!(
|
||||
ticket_id: ticket1.id,
|
||||
from: 'some_sender@example.com',
|
||||
to: 'some_recipient@example.com',
|
||||
subject: 'some subject',
|
||||
message_id: 'some@id',
|
||||
body: 'some message... 123',
|
||||
internal: false,
|
||||
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
||||
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
result = Ticket::Overviews.index(@agent1)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 1)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets])
|
||||
assert_equal(result[0][:tickets][0][:id], ticket1.id)
|
||||
|
||||
result = Ticket::Overviews.index(@agent2)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert(result[1])
|
||||
assert_equal(result[1][:overview][:name], 'My replacement Tickets')
|
||||
assert_equal(result[1][:overview][:view], 'my_replacement')
|
||||
assert_equal(result[1][:count], 1)
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets])
|
||||
assert_equal(result[1][:tickets][0][:id], ticket1.id)
|
||||
|
||||
result = Ticket::Overviews.index(@customer1)
|
||||
assert(result[0])
|
||||
assert_equal(result[0][:overview][:name], 'My Tickets')
|
||||
assert_equal(result[0][:overview][:view], 'my_tickets')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].blank?)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -352,16 +352,16 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
assert_equal(result[2][:count], 0)
|
||||
|
||||
result = Ticket::Overviews.index(@agent2)
|
||||
@@ -369,16 +369,16 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
ticket1 = Ticket.create!(
|
||||
title: 'overview test 1',
|
||||
@@ -408,17 +408,17 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket1.id)
|
||||
assert_equal(result[1][:count], 1)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
assert_equal(result[2][:count], 0)
|
||||
|
||||
result = Ticket::Overviews.index(@agent2)
|
||||
@@ -426,16 +426,16 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
travel 1.second # because of mysql millitime issues
|
||||
ticket2 = Ticket.create!(
|
||||
@@ -466,18 +466,18 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket1.id)
|
||||
assert_equal(result[1][:tickets][1][:id], ticket2.id)
|
||||
assert_equal(result[1][:count], 2)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
assert_equal(result[2][:count], 0)
|
||||
|
||||
result = Ticket::Overviews.index(@agent2)
|
||||
@@ -485,16 +485,16 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
ticket2.owner_id = @agent1.id
|
||||
ticket2.save!
|
||||
@@ -509,7 +509,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket1.id)
|
||||
assert_equal(result[1][:count], 1)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
|
||||
@@ -523,16 +523,16 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
travel 1.second # because of mysql millitime issues
|
||||
ticket3 = Ticket.create!(
|
||||
@@ -571,7 +571,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket1.id)
|
||||
assert_equal(result[1][:tickets][1][:id], ticket3.id)
|
||||
assert_equal(result[1][:count], 2)
|
||||
@@ -588,18 +588,18 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:id], @overview2.id)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:id], @overview3.id)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
@overview2.order = {
|
||||
by: 'created_at',
|
||||
@@ -619,7 +619,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket3.id)
|
||||
assert_equal(result[1][:tickets][1][:id], ticket1.id)
|
||||
assert_equal(result[1][:count], 2)
|
||||
@@ -636,18 +636,18 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:id], @overview2.id)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:id], @overview3.id)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
@overview2.order = {
|
||||
by: 'priority_id',
|
||||
@@ -667,7 +667,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket1.id)
|
||||
assert_equal(result[1][:tickets][1][:id], ticket3.id)
|
||||
assert_equal(result[1][:count], 2)
|
||||
@@ -684,18 +684,18 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:id], @overview2.id)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:id], @overview3.id)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
@overview2.order = {
|
||||
by: 'priority_id',
|
||||
@@ -715,7 +715,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket3.id)
|
||||
assert_equal(result[1][:tickets][1][:id], ticket1.id)
|
||||
assert_equal(result[1][:count], 2)
|
||||
@@ -732,18 +732,18 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:id], @overview2.id)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:id], @overview3.id)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
@overview2.order = {
|
||||
by: 'priority',
|
||||
@@ -763,7 +763,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket1.id)
|
||||
assert_equal(result[1][:tickets][1][:id], ticket3.id)
|
||||
assert_equal(result[1][:count], 2)
|
||||
@@ -780,18 +780,18 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:id], @overview2.id)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:id], @overview3.id)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
@overview2.order = {
|
||||
by: 'priority',
|
||||
@@ -811,7 +811,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert_not(result[1][:tickets].empty?)
|
||||
assert_not(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:tickets][0][:id], ticket3.id)
|
||||
assert_equal(result[1][:tickets][1][:id], ticket1.id)
|
||||
assert_equal(result[1][:count], 2)
|
||||
@@ -828,18 +828,18 @@ class TicketOverviewTest < ActiveSupport::TestCase
|
||||
assert_equal(result[0][:overview][:view], 'my_assigned')
|
||||
assert_equal(result[0][:count], 0)
|
||||
assert_equal(result[0][:tickets].class, Array)
|
||||
assert(result[0][:tickets].empty?)
|
||||
assert(result[0][:tickets].blank?)
|
||||
assert_equal(result[1][:overview][:id], @overview2.id)
|
||||
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
|
||||
assert_equal(result[1][:overview][:view], 'all_unassigned')
|
||||
assert_equal(result[1][:tickets].class, Array)
|
||||
assert(result[1][:tickets].empty?)
|
||||
assert(result[1][:tickets].blank?)
|
||||
assert_equal(result[1][:count], 0)
|
||||
assert_equal(result[2][:overview][:id], @overview3.id)
|
||||
assert_equal(result[2][:overview][:name], 'My Tickets 2')
|
||||
assert_equal(result[2][:overview][:view], 'my_tickets_2')
|
||||
assert_equal(result[2][:tickets].class, Array)
|
||||
assert(result[2][:tickets].empty?)
|
||||
assert(result[2][:tickets].blank?)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class UserOutOfOfficeTest < ActiveSupport::TestCase
|
||||
email: 'user-out_of_office-agent1@example.com',
|
||||
password: 'agentpw',
|
||||
active: true,
|
||||
out_of_office: false,
|
||||
roles: roles,
|
||||
groups: groups,
|
||||
)
|
||||
@@ -24,6 +25,7 @@ class UserOutOfOfficeTest < ActiveSupport::TestCase
|
||||
email: 'user-out_of_office-agent2@example.com',
|
||||
password: 'agentpw',
|
||||
active: true,
|
||||
out_of_office: false,
|
||||
roles: roles,
|
||||
groups: groups,
|
||||
)
|
||||
@@ -34,6 +36,7 @@ class UserOutOfOfficeTest < ActiveSupport::TestCase
|
||||
email: 'user-out_of_office-agent3@example.com',
|
||||
password: 'agentpw',
|
||||
active: true,
|
||||
out_of_office: false,
|
||||
roles: roles,
|
||||
groups: groups,
|
||||
)
|
||||
@@ -99,6 +102,9 @@ class UserOutOfOfficeTest < ActiveSupport::TestCase
|
||||
travel 2.days
|
||||
|
||||
assert(@agent1.out_of_office?)
|
||||
assert(@agent1.out_of_office_agent_of.blank?)
|
||||
assert_equal(1, @agent2.out_of_office_agent_of.count)
|
||||
assert_equal(@agent1.id, @agent2.out_of_office_agent_of[0].id)
|
||||
|
||||
travel 1.day
|
||||
|
||||
@@ -116,6 +122,9 @@ class UserOutOfOfficeTest < ActiveSupport::TestCase
|
||||
|
||||
assert_not(@agent2.out_of_office_agent)
|
||||
|
||||
assert_equal(0, @agent1.out_of_office_agent_of.count)
|
||||
assert_equal(0, @agent2.out_of_office_agent_of.count)
|
||||
|
||||
@agent2.out_of_office = true
|
||||
@agent2.out_of_office_start_at = Time.zone.now
|
||||
@agent2.out_of_office_end_at = Time.zone.now + 4.days
|
||||
@@ -126,6 +135,23 @@ class UserOutOfOfficeTest < ActiveSupport::TestCase
|
||||
|
||||
assert_equal(@agent2.out_of_office_agent.id, @agent3.id)
|
||||
|
||||
assert_equal(0, @agent1.out_of_office_agent_of.count)
|
||||
assert_equal(0, @agent2.out_of_office_agent_of.count)
|
||||
assert_equal(1, @agent3.out_of_office_agent_of.count)
|
||||
assert_equal(@agent2.id, @agent3.out_of_office_agent_of[0].id)
|
||||
|
||||
travel 4.days
|
||||
|
||||
assert_equal(0, @agent1.out_of_office_agent_of.count)
|
||||
assert_equal(0, @agent2.out_of_office_agent_of.count)
|
||||
assert_equal(1, @agent3.out_of_office_agent_of.count)
|
||||
assert_equal(@agent2.id, @agent3.out_of_office_agent_of[0].id)
|
||||
|
||||
travel 1.day
|
||||
|
||||
assert_equal(0, @agent1.out_of_office_agent_of.count)
|
||||
assert_equal(0, @agent2.out_of_office_agent_of.count)
|
||||
assert_equal(0, @agent3.out_of_office_agent_of.count)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user