Compare commits

...

286 Commits

Author SHA1 Message Date
Muhammad Nuzaihan
4d3ac8f340 fixes #1756 with security issue of content injection in excel generated data 2018-01-23 17:30:33 +08:00
Martin Edenhofer
7b319d5ffb Updated changes. 2017-10-25 10:27:53 +02:00
André Bauer
accea5dff8 remove ruby 2.3.0 dir again 2017-10-19 19:58:21 +02:00
Martin Edenhofer
49e20d3655 Fixed issue #1538 - Unable to reindex tickets / elasticsearch error "MapperParsingException[failed to parse [article.ticket]]; nested: JsonParseException[Current token (START_OBJECT) not of boolean type". 2017-10-16 00:37:45 +02:00
Martin Edenhofer
f6ab1cf3c9 Provide ticket sidebar with tags and links from origin all=true ticket request. 2017-10-16 00:35:13 +02:00
André Bauer
f21b59ff7f fixed travis commit message quotes 2017-10-11 21:48:54 +02:00
André Bauer
2f2c3f41b0 added debian 9 support for packager.io 2017-10-11 21:06:53 +02:00
André Bauer
c1912c7d3c added zammad commit message to travis-ci docker triggers 2017-10-11 21:00:58 +02:00
Martin Edenhofer
98c9e056ea Follow up for issue #1276 - only set channel data if matrix exists. 2017-10-10 05:35:45 +02:00
Dinh Ngoc Hien
c8777a4b1e Update notification preferences when nothing is selected #1276 (#1514) 2017-10-10 05:35:38 +02:00
Martin Edenhofer
2eb4fd3e00 Improved reply to / recipient tests (added real name for tests). 2017-10-09 02:26:40 +02:00
Martin Edenhofer
dcc3a96efa Improved reply to / recipient tests. 2017-10-09 01:38:06 +02:00
Martin Edenhofer
8eb858ffc8 Fixed issue #1303 - ReplyAll - moves recipients from "To" to "CC". 2017-10-09 01:09:35 +02:00
Martin Edenhofer
921e571ae2 Fixed issue #986 - Zammad sends reply to itself. 2017-10-08 23:56:53 +02:00
André Bauer
66e7b87ba3 Merge branch 'stable' of git.znuny.com:zammad/zammad into stable 2017-10-03 00:17:42 +02:00
André Bauer
c98a4ac48b added missing quotes to travis-ci script 2017-10-03 00:11:44 +02:00
André Bauer
a257451ee0 added real zammd version as image tag 2017-10-03 00:09:28 +02:00
Martin Edenhofer
e3cfd1db35 Moved to mock api call for geo_location (for stable tests). 2017-10-02 20:33:30 +02:00
Martin Edenhofer
a7efa39a8e Improved error handling of channel streaming. 2017-10-02 15:13:35 +02:00
Martin Edenhofer
8620664c07 Fixed issue #1406 - Channel: Twitter streaming is complaining about. 2017-10-02 14:31:59 +02:00
Martin Edenhofer
62d86ffe12 Added tests for new icinga mail templates. 2017-09-28 21:09:44 +02:00
Martin Edenhofer
4ab13580c5 Fixed issue #1482 - Tickets sent by agent via email into Zammad have state open (not new). 2017-09-27 20:14:49 +02:00
Martin Edenhofer
038ded71f5 Fixed migration (was backport from RoR 5.1). 2017-09-27 11:07:58 +02:00
Martin Edenhofer
0206b17cf9 Added migration to correct send_no_auto_response_reg_exp value. It's already changed in db/seeds/settings.rb 2017-09-27 11:03:30 +02:00
Martin Edenhofer
7360844c8e Fixed unit tests. 2017-09-26 23:07:23 +02:00
Martin Edenhofer
342d826ab2 Improved test. 2017-09-26 22:29:51 +02:00
Martin Edenhofer
3171fedfc6 Updated gem nokogiri. 2017-09-26 20:30:51 +02:00
Martin Edenhofer
ae46d0accb Fixed issue #1439 - Icinga/Nagios Integration: No longer accepts regex in "Sender". 2017-09-26 20:27:10 +02:00
Thorsten Eckel
c4ce1caae5 Avoid unneeded memory consumption for Zendesk import. 2017-09-15 11:36:29 +02:00
Thorsten Eckel
0e32d9dc37 Fixed issue #1424 - Method lookup in wrong class namespace. 2017-09-15 10:20:46 +02:00
Martin Edenhofer
eb5fe7fdff Fixed issue #1398 - Profile not clickable on browsers with touch events or on some Chrome for Windows. 2017-09-12 16:40:48 +02:00
Thorsten Eckel
61401f762c Fixed issue #1406 - Exchange integration wizard fails if server does not support autodiscovery. 2017-09-12 11:48:22 +02:00
Martin Edenhofer
d6e5e1eaee Improved migration. 2017-09-11 07:58:03 +02:00
Martin Edenhofer
d5f7609a26 Improved direct group assignment for agents. 2017-09-11 04:47:38 +02:00
Martin Edenhofer
b390456159 Improved tests for out of office feature and related controllers. 2017-09-11 04:30:16 +02:00
Martin Edenhofer
76630be5ee Moved to rails 4.2 test file format. 2017-09-10 20:50:07 +02:00
Martin Edenhofer
b568f354b7 Added tests for taskbar controller. 2017-09-10 19:43:52 +02:00
Martin Edenhofer
f7e3412540 Fixed issue #1394 - Reindex elastic search not possible because of <null>/Twitter::NullObject. 2017-09-08 17:24:04 +02:00
Martin Edenhofer
bf36225be2 Fixed issue #1390 - Broken email with huge pdf inline (lead to huge article body with bigger then 7MB) is blocking rendering of web app. 2017-09-08 09:49:58 +02:00
Denny Bresch
a4339bd8fa #1193 - quote name for special character. 2017-09-06 16:19:14 +02:00
Martin Edenhofer
1910f8da85 Updated for release. 2017-09-06 15:35:20 +02:00
Thorsten Eckel
db8b85f767 Merge branch 'develop' 2017-09-06 15:01:15 +02:00
Martin Edenhofer
f1664c3841 Merge branch 'develop' 2017-09-05 21:54:52 +02:00
Martin Edenhofer
151c298056 Merge branch 'develop' 2017-08-31 09:05:09 +02:00
Martin Edenhofer
38f51ce6b2 Merge branch 'develop' 2017-08-28 11:44:51 +02:00
Martin Edenhofer
434f3e963e Merge branch 'develop' 2017-08-25 05:38:22 +02:00
Martin Edenhofer
b853dd1597 Merge branch 'develop' 2017-08-24 01:00:34 +02:00
Martin Edenhofer
3b5b6f612f Merge branch 'develop' 2017-08-23 17:16:20 +02:00
Martin Edenhofer
962043c520 Merge branch 'develop' 2017-08-22 14:58:32 +02:00
Martin Edenhofer
8394be87ed Merge branch 'develop' 2017-08-14 11:32:42 +02:00
Martin Edenhofer
5b547ceb3c Merge branch 'develop' 2017-08-13 11:55:41 +02:00
Martin Edenhofer
789ee08485 Merge branch 'develop' 2017-08-11 12:08:58 +02:00
Martin Edenhofer
490937859a Merge branch 'develop' 2017-07-29 13:03:58 +02:00
Martin Edenhofer
dfc306a2ac Merge branch 'develop' 2017-07-28 13:57:37 +02:00
Martin Edenhofer
dea06015a1 Merge branch 'develop' 2017-07-28 11:48:50 +02:00
Martin Edenhofer
2711e3392d Merge branch 'develop' 2017-07-27 06:38:29 +02:00
Martin Edenhofer
f5e1fe0c2f Merge branch 'develop' 2017-07-24 15:26:00 +02:00
Martin Edenhofer
dbbb4ac7a1 Merge branch 'develop' 2017-07-21 15:09:34 +02:00
Martin Edenhofer
8ec2ffdde1 Merge branch 'develop' 2017-07-20 17:22:54 +02:00
Martin Edenhofer
b76664e7ae Merge branch 'develop' 2017-07-20 15:58:47 +02:00
Martin Edenhofer
704c537749 Merge branch 'develop' 2017-07-20 15:00:37 +02:00
Martin Edenhofer
633c99bb72 Merge branch 'develop' 2017-07-19 11:07:56 +02:00
Martin Edenhofer
44051d8023 Merge branch 'develop' 2017-07-16 18:15:24 +02:00
Martin Edenhofer
50dc143c1b Merge branch 'develop' 2017-07-14 15:58:16 +02:00
Martin Edenhofer
6d44275869 Merge branch 'develop' 2017-07-13 20:53:59 +02:00
Martin Edenhofer
b1b3bf6d3c Merge branch 'develop' 2017-07-12 13:30:01 +02:00
Martin Edenhofer
77081ecd4d Merge branch 'develop' 2017-07-04 16:05:04 +02:00
Martin Edenhofer
b823b960d8 Merge branch 'develop' 2017-06-30 08:32:40 +02:00
Martin Edenhofer
e08f634125 Merge branch 'develop' 2017-06-21 09:10:41 +02:00
Martin Edenhofer
ab5e8d9338 Merge branch 'develop' 2017-06-19 20:01:15 +02:00
Martin Edenhofer
9a29ea8f14 Merge branch 'develop' 2017-06-13 02:40:32 +02:00
Martin Edenhofer
cf87e00b21 Merge branch 'develop' 2017-06-13 01:57:29 +02:00
Martin Edenhofer
6233b0a9ec Merge branch 'develop' 2017-06-01 18:32:42 +02:00
Martin Edenhofer
04b1bc638c Merge branch 'develop' 2017-05-29 06:57:52 +02:00
Martin Edenhofer
a78938f7c2 Merge branch 'develop' 2017-05-23 11:05:51 +02:00
Martin Edenhofer
1d23308454 Merge branch 'develop' 2017-05-23 10:30:03 +02:00
Martin Edenhofer
6bc9c00ab8 Merge branch 'develop' 2017-05-16 15:41:06 +02:00
Martin Edenhofer
bc131742be Merge branch 'develop' 2017-05-16 14:22:22 +02:00
Martin Edenhofer
5a12aa0167 Merge branch 'develop' 2017-05-16 01:47:49 +02:00
Martin Edenhofer
b3ab9d0055 Merge branch 'develop' 2017-05-16 00:57:14 +02:00
Martin Edenhofer
1bde8f91bc Merge branch 'develop' 2017-05-15 15:31:15 +02:00
Martin Edenhofer
47ef1b7e5d Merge branch 'develop' 2017-05-11 23:54:32 +02:00
Martin Edenhofer
88c7816f5c Merge branch 'develop' 2017-05-08 17:09:55 +02:00
Martin Edenhofer
82339c6c18 Merge branch 'develop' 2017-05-08 16:56:10 +02:00
Martin Edenhofer
04a609c3a8 Merge branch 'develop' 2017-05-05 15:23:04 +02:00
Martin Edenhofer
60430139aa Merge branch 'develop' 2017-05-02 10:45:06 +02:00
Martin Edenhofer
7d1c655ee3 Merge branch 'develop' 2017-04-28 16:50:31 +02:00
Martin Edenhofer
e34c1c9b79 Merge branch 'develop' 2017-04-28 12:48:40 +02:00
Thorsten Eckel
3c430b35ef Fixed issue #906 - Doorkeeper tokens should be refreshable but aren't. 2017-04-24 13:58:03 +02:00
Rolf Schmidt
7ad3c70311 Fixed issue #975 - Exception in OTRS Import. 2017-04-24 11:58:07 +02:00
Martin Edenhofer
c270bc1e67 Merge branch 'develop' 2017-04-21 14:36:50 +02:00
Thorsten Eckel
3d19bfdcf7 Fixed issue #912 - Long Twitter direct messages are shown as link after 140 chars. 2017-04-21 13:58:33 +02:00
Martin Edenhofer
dc4a55cc5c Merge branch 'develop' 2017-04-21 12:38:25 +02:00
Martin Edenhofer
6e8886277d Merge branch 'develop' 2017-04-21 06:17:09 +02:00
Martin Edenhofer
74e96e92b2 Merge branch 'develop' 2017-04-20 00:22:13 +02:00
Martin Edenhofer
287b1f0d01 Merge branch 'develop' 2017-04-18 09:57:19 +02:00
Martin Edenhofer
feee46e29b Merge branch 'develop' 2017-04-08 08:32:45 +02:00
Martin Edenhofer
991efe36e4 Merge branch 'develop' 2017-04-05 22:23:05 +02:00
Martin Edenhofer
a2aacb20fa Merge branch 'develop' 2017-03-31 16:20:21 +02:00
Martin Edenhofer
592e978062 Merge branch 'develop' 2017-03-31 14:01:20 +02:00
Martin Edenhofer
a4769c8bc7 Merge branch 'develop' 2017-03-27 14:49:54 +02:00
Martin Edenhofer
d1378d53f1 Merge branch 'develop' 2017-03-25 14:44:10 +01:00
Martin Edenhofer
f0b91d3269 Merge branch 'develop' 2017-03-23 21:10:03 +01:00
Martin Edenhofer
48c77dd688 Merge branch 'develop' 2017-03-21 16:06:51 +01:00
Martin Edenhofer
d86d904395 Merge branch 'develop' 2017-03-21 10:05:58 +01:00
Martin Edenhofer
5efc455f0c Merge branch 'develop' 2017-03-18 23:16:57 +01:00
Martin Edenhofer
be3bbeba20 Merged develop into master. 2017-03-18 13:45:42 +01:00
Martin Edenhofer
31ab1b614c Merge branch 'develop' 2017-03-17 08:09:20 +01:00
Martin Edenhofer
4bc5fa4693 Merge branch 'develop' 2017-03-14 19:24:36 +01:00
Martin Edenhofer
418a64f401 Merge branch 'develop' 2017-03-14 13:33:37 +01:00
Martin Edenhofer
abe46270a6 Merge branch 'develop' 2017-03-03 10:54:22 +01:00
Martin Edenhofer
1249a77150 Merge branch 'develop' 2017-03-03 09:46:01 +01:00
Martin Edenhofer
cfa6b9ed16 Merge branch 'develop' 2017-02-24 23:19:06 +01:00
Rolf Schmidt
aa2303f999 Merge branch 'develop' 2017-02-24 09:27:43 +01:00
Martin Edenhofer
d5616684f8 Merge branch 'develop' 2017-02-22 07:12:36 +01:00
Martin Edenhofer
0f75ba57bd Merge branch 'develop' 2017-02-15 17:42:20 +01:00
Martin Edenhofer
c83675bbba Merge branch 'develop' 2017-02-15 13:39:29 +01:00
Martin Edenhofer
17d16d405c Merge branch 'develop' 2017-02-06 11:48:59 +01:00
Martin Edenhofer
76795ca302 Merge branch 'develop' 2017-02-06 00:16:24 +01:00
Martin Edenhofer
5d145bd09f Merge branch 'develop' 2017-02-05 19:26:16 +01:00
Martin Edenhofer
82471552a8 Merge branch 'develop' 2017-02-01 23:24:18 +01:00
Rolf Schmidt
dc60574129 Merge branch 'develop' 2017-01-23 15:05:52 +01:00
Martin Edenhofer
d33a96dee2 Merge branch 'develop' 2017-01-19 06:18:19 +01:00
Martin Edenhofer
0098d4ab82 Merge branch 'develop' 2017-01-17 15:57:42 +01:00
Martin Edenhofer
bb03b5eab8 Merge branch 'develop' 2017-01-17 12:41:03 +01:00
Martin Edenhofer
17eb0f84ec Merge branch 'develop' 2017-01-17 08:56:18 +01:00
Martin Edenhofer
56d0523c1f Merge branch 'develop' 2017-01-04 15:21:24 +01:00
Martin Edenhofer
9fa18758bc Merge branch 'develop' 2016-12-21 14:12:06 +01:00
Martin Edenhofer
d2f2c55b06 Merge branch 'develop' 2016-12-21 08:50:52 +01:00
Martin Edenhofer
f4a409bd6e Merge branch 'develop' 2016-12-21 00:13:59 +01:00
Martin Edenhofer
971b724d4c Merge branch 'develop' 2016-12-18 12:45:13 +01:00
Martin Edenhofer
3263c6f2ba Merge branch 'develop' 2016-12-16 15:49:33 +01:00
Martin Edenhofer
359b026535 Merge branch 'develop' 2016-12-15 17:16:38 +01:00
Martin Edenhofer
c63518fd3e Merge branch 'develop' 2016-12-14 16:06:09 +01:00
Martin Edenhofer
24ba3cff44 Merge branch 'develop' 2016-12-06 13:29:23 +01:00
Martin Edenhofer
f7066e1995 Merge branch 'develop' 2016-12-06 12:23:13 +01:00
Martin Edenhofer
6844499309 Merge branch 'develop' 2016-11-25 16:59:16 +01:00
Martin Edenhofer
afa3e47eab Merge branch 'develop' 2016-11-25 14:22:00 +01:00
Martin Edenhofer
5f3e9298f1 Merge branch 'develop' 2016-11-24 16:03:45 +01:00
Martin Edenhofer
e4dd303357 Merge branch 'develop' 2016-11-21 17:07:30 +01:00
Martin Edenhofer
2fa06eee3d Merge branch 'develop' 2016-11-21 08:58:54 +01:00
Martin Edenhofer
dc0ac03334 Merge branch 'develop' 2016-11-19 16:42:10 +01:00
Martin Edenhofer
c5a1d69ec5 Merge branch 'develop' 2016-11-19 16:35:07 +01:00
Martin Edenhofer
9891981f92 Merge branch 'develop' 2016-11-17 15:12:22 +01:00
Martin Edenhofer
2c721f71e7 Merge branch 'develop' 2016-11-17 10:03:34 +01:00
Martin Edenhofer
0b4a4be0b7 Merge branch 'develop' 2016-11-16 23:21:05 +01:00
Martin Edenhofer
b13bb2fcaf Added change log. 2016-11-14 08:37:51 +01:00
Martin Edenhofer
d9ba369fcf Merge branch 'develop' 2016-11-14 08:36:58 +01:00
Martin Edenhofer
3adece0db5 Merge branch 'develop' 2016-11-14 00:03:57 +01:00
Martin Edenhofer
7101ffc277 Merge branch 'develop' 2016-11-12 16:44:43 +01:00
Martin Edenhofer
19a7a439b8 Merge branch 'develop' 2016-11-11 19:29:56 +01:00
Martin Edenhofer
e728320241 Merge branch 'develop' 2016-10-30 07:08:08 +01:00
Martin Edenhofer
1bc17afc53 Merge branch 'master' of git.znuny.com:zammad/zammad 2016-10-30 07:07:39 +01:00
Thorsten Eckel
ee5c58f53a Fixed bug: Zip archive doesn't contain sub folder content. 2016-10-19 08:25:51 +02:00
Martin Edenhofer
9bdf6d7b81 Merge branch 'develop' 2016-10-19 07:50:08 +02:00
Thorsten Eckel
5754fdccdd Working on CI tag/branch pushing, syncing, deployment issue. 2016-10-19 07:32:05 +02:00
Thorsten Eckel
4901050d57 Working on issue not syncing to github correctly. 2016-10-19 07:25:18 +02:00
Martin Edenhofer
7980c8cb93 Merge branch 'develop' 2016-10-19 07:21:10 +02:00
Martin Edenhofer
7e7088388d Merge branch 'develop' 2016-10-17 16:43:54 +02:00
Martin Edenhofer
2f6e52d63d Merge branch 'develop' 2016-09-28 16:08:28 +02:00
Martin Edenhofer
bde8babb43 Merge branch 'develop' 2016-09-28 08:53:02 +02:00
Martin Edenhofer
d6d68fb363 Merge branch 'develop' 2016-09-19 07:47:06 +02:00
Martin Edenhofer
814ffed0f0 Merge branch 'develop' 2016-09-15 22:11:57 +02:00
Thorsten Eckel
b013fb8cbc Merge branch 'private-otrs_import_df' 2016-09-13 12:06:56 +02:00
Martin Edenhofer
fbd8e63592 Merge branch 'develop' 2016-09-13 08:55:38 +02:00
Martin Edenhofer
e8ad3965d7 Removed build badged. 2016-09-10 16:47:33 +02:00
Martin Edenhofer
e00981ee23 Merge branch 'master' of git.znuny.com:zammad/zammad 2016-09-10 00:05:18 +02:00
Martin Edenhofer
4e74c6beda Merge branch 'develop' 2016-09-10 00:04:28 +02:00
Thorsten Eckel
2d09e556d7 Added deployment of archives. 2016-09-07 15:31:28 +02:00
Martin Edenhofer
8cd39398d6 Merge branch 'develop' 2016-09-06 16:07:33 +02:00
Martin Edenhofer
94c616ffe3 Merge branch 'develop' 2016-08-25 10:00:36 +02:00
Martin Edenhofer
57649bb888 Merge branch 'develop' 2016-08-23 09:49:40 +02:00
Martin Edenhofer
7e9442ec4a Merge branch 'develop' 2016-08-21 21:38:00 +02:00
Martin Edenhofer
521e6265fd Merge branch 'develop' 2016-08-19 13:58:26 +02:00
Martin Edenhofer
a42a9689fc Merge branch 'develop' 2016-08-19 13:48:46 +02:00
Martin Edenhofer
d9ffb8001d Merge branch 'develop' 2016-07-28 13:39:08 +02:00
Martin Edenhofer
ec86d26ae6 Merge branch 'develop' 2016-07-24 23:52:04 +02:00
Martin Edenhofer
e00efc821a Merge branch 'develop' 2016-07-18 19:35:51 +02:00
Martin Edenhofer
f04be8ca93 Merge branch 'develop' 2016-07-12 20:50:32 +02:00
Martin Edenhofer
c7ff6a323a Merge branch 'develop' 2016-07-08 11:28:43 +02:00
Martin Edenhofer
34833416bc Merge branch 'develop' 2016-06-30 09:50:09 +02:00
Martin Edenhofer
d7a6e28d81 Merge branch 'develop' 2016-06-29 00:18:44 +02:00
Martin Edenhofer
c13531d720 Merge branch 'develop' 2016-06-27 11:28:22 +02:00
Martin Edenhofer
4a40cced7d Merge branch 'develop' 2016-06-26 08:07:04 +02:00
Martin Edenhofer
450de10cc7 Merge branch 'develop' 2016-06-23 08:24:56 +02:00
Martin Edenhofer
5e587a455c Merge branch 'develop' 2016-06-22 08:54:24 +02:00
Martin Edenhofer
04aef9b258 Merge branch 'develop' 2016-06-20 15:52:43 +02:00
Martin Edenhofer
9b72557d13 Merge branch 'develop' 2016-06-15 06:58:36 +02:00
Martin Edenhofer
a5e0bc30c1 Merge branch 'develop' 2016-06-09 13:40:27 +02:00
Martin Edenhofer
458c166e9f Merge branch 'develop' 2016-06-08 16:43:29 +02:00
Martin Edenhofer
3373e5d141 Merge branch 'develop' 2016-06-08 10:48:12 +02:00
Martin Edenhofer
8f6b8de617 Merge branch 'develop' 2016-06-08 07:42:19 +02:00
Martin Edenhofer
22b21a82e7 Merge branch 'develop' 2016-06-07 07:02:25 +02:00
Martin Edenhofer
b4151b299f Merge branch 'develop' 2016-06-04 02:23:14 +02:00
Martin Edenhofer
e969fca8c4 Merge branch 'develop' 2016-06-03 07:08:59 +02:00
Martin Edenhofer
e59993a5d6 Merge branch 'develop' 2016-05-31 08:31:56 +02:00
Martin Edenhofer
3adc9d4cf1 Merge branch 'develop' 2016-05-30 13:21:46 +02:00
Martin Edenhofer
c1153250cb Merge branch 'develop' 2016-05-30 00:00:52 +02:00
Martin Edenhofer
7718dc28d3 Merge branch 'develop'
# Conflicts:
#	README.md
2016-05-27 17:30:22 +02:00
Martin Edenhofer
1894580ae6 Updated badge url. 2016-05-27 17:28:26 +02:00
Martin Edenhofer
11df714bbf Merge branch 'develop' 2016-05-20 15:18:36 +02:00
Martin Edenhofer
72adee37a8 Merge branch 'develop' 2016-05-20 06:10:19 +02:00
Martin Edenhofer
0e8f460d73 Merge branch 'develop' 2016-05-06 16:25:09 +02:00
Martin Edenhofer
4e62d5d0d2 Merge branch 'develop' 2016-05-06 09:40:16 +02:00
Martin Edenhofer
004e5ba457 Merge branch 'develop' 2016-05-04 12:13:34 +02:00
Martin Edenhofer
f1a8a8da6f Merge branch 'develop' 2016-05-03 16:01:03 +02:00
Martin Edenhofer
bfb44d1189 Merge branch 'develop' 2016-05-01 14:18:26 +02:00
Martin Edenhofer
d64afd8d97 Merge branch 'develop' 2016-04-14 17:47:03 +02:00
Martin Edenhofer
97cfdb9c4c Merge branch 'develop' 2016-04-12 08:25:06 +02:00
Martin Edenhofer
bb336f63f1 Merge branch 'develop' 2016-03-25 18:23:12 +01:00
Martin Edenhofer
80da08f447 Merge branch 'develop' 2016-03-23 09:31:02 +01:00
Martin Edenhofer
a6db2b7c90 Merge branch 'develop' 2016-03-17 17:45:04 +01:00
Martin Edenhofer
e1c3f83324 Merge branch 'develop' 2016-03-17 10:41:20 +01:00
Martin Edenhofer
8545a44ad1 Merge branch 'develop' 2016-03-16 11:17:22 +01:00
Martin Edenhofer
cfbe7510a2 Merge branch 'develop' 2016-03-16 09:10:05 +01:00
Martin Edenhofer
1479c32edb Merge branch 'develop' 2016-03-15 00:34:36 +01:00
Martin Edenhofer
a9d428bf5a Merge branch 'develop' 2016-03-11 10:38:15 +01:00
Martin Edenhofer
3a62eb11da Merge branch 'develop' 2016-03-04 07:07:55 +01:00
Martin Edenhofer
2df5f81029 Merge branch 'develop' 2016-03-03 03:42:27 +01:00
Martin Edenhofer
b88c23bc92 Merge branch 'develop' 2016-02-28 00:16:49 +01:00
Martin Edenhofer
4144fbaf5b Merge branch 'develop' 2016-02-25 23:46:10 +01:00
Martin Edenhofer
86ddc93f52 Merge branch 'develop' 2016-02-23 14:29:01 +01:00
Martin Edenhofer
8a1b583535 Merge branch 'develop' 2016-02-23 01:38:39 +01:00
Martin Edenhofer
7623628b86 Merge branch 'develop' 2016-02-12 08:53:57 +01:00
Martin Edenhofer
f753bc203f Merge branch 'develop' 2016-02-11 13:25:39 +01:00
Martin Edenhofer
ff9ba10705 Merge branch 'develop' 2016-02-11 02:11:06 +01:00
Martin Edenhofer
fdc25bdc26 Merge branch 'develop' 2016-02-09 01:36:03 +01:00
Martin Edenhofer
d154a587ad Merge branch 'develop' 2016-02-04 12:55:30 +01:00
Martin Edenhofer
a9cc4f037b Merge branch 'develop' 2016-02-04 10:41:41 +01:00
Martin Edenhofer
50d6679189 Merge branch 'develop' 2016-02-03 13:19:51 +01:00
Martin Edenhofer
b82cf4e1d6 Merge branch 'develop' 2016-01-29 15:19:23 +01:00
Martin Edenhofer
fa6eaa6710 Merge branch 'develop'
# Conflicts:
#	README.md
2016-01-26 19:59:34 +01:00
Martin Edenhofer
2ca17ac1bd Updated build status url. 2016-01-26 13:57:24 +01:00
Martin Edenhofer
862ecd7d36 Merge branch 'develop' 2016-01-22 14:47:53 +01:00
Martin Edenhofer
e38b72593b Merge branch 'develop' 2016-01-12 11:14:24 +01:00
Martin Edenhofer
81883cae8f Revert "update terminal start script"
This reverts commit 63770c9cbf.
2016-01-12 11:13:40 +01:00
Felix Niklas
63770c9cbf update terminal start script 2016-01-12 10:58:46 +01:00
Martin Edenhofer
aaf5da55a0 Merge branch 'develop' 2015-12-28 11:33:58 +01:00
Martin Edenhofer
8455d9ae17 Merge branch 'develop' 2015-12-21 16:03:31 +01:00
Martin Edenhofer
839a8e8eb4 Merge branch 'develop' 2015-12-13 15:26:35 +01:00
Martin Edenhofer
7aa3f6eaee Updated build url. 2015-12-10 09:44:16 +01:00
Martin Edenhofer
219abc69a3 Merge branch 'develop' 2015-12-10 09:21:59 +01:00
Martin Edenhofer
595822c928 Merge branch 'develop' 2015-12-02 02:27:40 +01:00
Martin Edenhofer
32112937b1 Merge branch 'develop' 2015-12-02 01:41:10 +01:00
Martin Edenhofer
a0889c5b99 Merge branch 'develop' 2015-12-01 22:03:52 +01:00
Martin Edenhofer
4bc281bfb0 Merge branch 'develop' 2015-12-01 20:04:51 +01:00
Martin Edenhofer
e12ec607d7 Merge branch 'develop' 2015-11-30 15:11:25 +01:00
Martin Edenhofer
545f393293 Merge branch 'develop' 2015-11-30 01:24:11 +01:00
Martin Edenhofer
4efa152e93 Merge branch 'master' of github.com:martini/zammad 2015-11-24 03:39:17 +01:00
Martin Edenhofer
0ebda4f32a Merge branch 'develop'
Conflicts:
	README.md
	script/websocket-server.rb
2015-11-24 03:38:55 +01:00
Martin Edenhofer
b94f54799f Merge branch 'develop' of http://ci.lab.zammad.com/core/zammad 2015-11-24 03:07:38 +01:00
Martin Edenhofer
99f0b4e2e2 Merge branch 'develop' 2015-11-19 14:27:39 +01:00
Martin Edenhofer
53cb906733 Merge branch 'develop' 2015-11-01 01:49:02 +01:00
Martin Edenhofer
2c31fe8a84 Merge branch 'develop' 2015-10-31 23:40:10 +01:00
Martin Edenhofer
6c5e673a2c Merge branch 'develop' 2015-10-02 15:31:54 +02:00
Martin Edenhofer
574b891a33 Merge branch 'develop' 2015-09-28 02:12:31 +02:00
Martin Edenhofer
9ef1d3f58b Merge branch 'develop'
Conflicts:
	extras/zammad.spec
2015-09-28 01:24:03 +02:00
Martin Edenhofer
8b3f432746 Merge branch 'develop' 2015-09-01 03:15:03 +02:00
Martin Edenhofer
14a9df261e Merge branch 'develop' 2015-08-31 15:11:27 +02:00
Martin Edenhofer
5620a2847b Merge branch 'develop' 2015-08-31 12:42:04 +02:00
Martin Edenhofer
13aaae3835 Merge branch 'develop' 2015-08-30 09:19:02 +02:00
Martin Edenhofer
2d4fcd3a4f Merge branch 'develop' 2015-08-30 08:22:27 +02:00
Martin Edenhofer
f4f70b0bc2 Merge branch 'develop' 2015-08-30 01:36:18 +02:00
Martin Edenhofer
1d279bb88f Merge branch 'develop' 2015-08-30 01:32:10 +02:00
Martin Edenhofer
fe4757cbf9 Merge branch 'develop' 2015-08-21 17:03:28 +02:00
me
1d476f163e Use also mysql on test. 2015-08-21 16:53:38 +02:00
Martin Edenhofer
513701b6d2 Merge branch 'develop'
Conflicts:
	README.md
	script/websocket-server.rb
2015-08-11 15:17:14 +02:00
Thorsten Eckel
1a1b661714 Fixed typo. 2015-07-16 09:50:18 +02:00
Roy Kaldung
6081a98279 Update README.md 2015-01-12 12:39:26 +01:00
Roy Kaldung
157991c33d Update README.md 2015-01-12 12:38:11 +01:00
Roy Kaldung
ef67be9170 We like to know if it's starting or stopping 2014-11-06 17:24:45 +01:00
Martin Edenhofer
39f3819303 Merge branch 'develop' 2014-11-06 16:13:39 +01:00
Martin Edenhofer
7a676bd563 Merge branch 'develop' into 'master', goto next level.
Conflicts:
	Gemfile
	README.md
	extras/zammad.spec
2014-10-05 20:42:39 +02:00
Roy Kaldung
80c54daa03 Delete httpd.spec 2014-05-02 12:47:51 +02:00
Roy Kaldung
99c88cde62 Delete apr.spec 2014-05-02 12:47:41 +02:00
Roy Kaldung
17d113ca20 removed old file 2014-05-02 12:47:18 +02:00
Martin Edenhofer
2fbf4324a7 Update Gemfile 2014-02-22 14:46:25 +01:00
Roy Kaldung
b46c390d30 minor change to test GitHub hook (automatic deploy) 2013-12-04 18:22:21 +01:00
Martin Edenhofer
3c3651bc52 Merge branch 'develop'
Conflicts:
	app/controllers/tickets_controller.rb
2013-09-20 13:52:53 +02:00
Roy Kaldung
abeeb5e66d Updated Build Status image URL 2013-08-23 21:00:55 +02:00
rkaldung
09d61ce4c8 added dependency 2013-08-14 20:38:09 +02:00
rkaldung
cc1c4495b2 Merge branch 'master' of github.com:martini/zammad 2013-08-14 20:27:15 +02:00
rkaldung
7b1ec4d17f modified install section 2013-08-14 20:26:25 +02:00
rkaldung
99b3201ccf added initial spec for rpm 2013-08-14 20:20:55 +02:00
Roy Kaldung
d4205d9ba6 removed Windows ;-) 2013-08-14 20:08:23 +02:00
Roy Kaldung
8d57633390 minor changes on comments 2013-08-05 19:18:52 +02:00
72 changed files with 2978 additions and 501 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1 +1 @@
1.6.x
2.0.x

View File

@@ -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 = ['-']

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -40,6 +40,5 @@ class App.Group extends App.Model
read: 'Read'
create: 'Create'
change: 'Change'
delete: 'Delete'
overview: 'Overview'
full: 'Full'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ class Integration::ExchangeController < ApplicationController
)
{
endpoint: client.autodiscover.ews_url,
endpoint: client.try(:autodiscover).try(:ews_url),
}
end
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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'],

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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)
});
}

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View 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

View 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

View 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

View File

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

View File

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