Compare commits

...

184 Commits

Author SHA1 Message Date
Martin Edenhofer
1974f9cee0 Fixed issue #838 - Disabled macros are still shown in ticket screen. 2017-03-14 13:34:43 +01:00
Martin Edenhofer
22ca95d5b1 Directory traversal vulnerability in rubyzip (CVE-2017-5946). 2017-03-03 11:00:02 +01:00
Martin Edenhofer
8ca3c54ff1 Updated CHANGELOG. 2017-02-15 14:14:12 +01:00
Thorsten Eckel
f0277013d5 Added the possiblity to inherit attribute filter and overwrite/extend via child methods. 2017-02-15 13:24:03 +01:00
Martin Edenhofer
d81e13c532 Fixed issue #719 - Ticket without Subject is blocked - can't do anything with it ("Title needed”). 2017-02-13 10:27:18 +01:00
Martin Edenhofer
8bdff80959 Fixed typo in filename of migration. 2017-02-09 13:33:05 +01:00
Martin Edenhofer
b3f1dced52 Fixed issue #650 - Unable to process emails with email addresses longer then 140 signs. 2017-02-09 13:24:38 +01:00
Thorsten Eckel
54dbd7efff Added backport to skip concerns in model lookup logic. 2017-02-06 14:52:20 +01:00
Martin Edenhofer
176379dc6d Moved to whitelist sanitizer. 2017-02-06 14:52:13 +01:00
Thorsten Eckel
0362f240d0 Added HTML Sanitzer concern for multi model usage. 2017-02-06 14:52:03 +01:00
Martin Edenhofer
c3822f195a Fixed issue #740 - Unable to process emails if "Additional follow-up detection" was checked and unchecked again. 2017-02-06 11:49:40 +01:00
Thorsten Eckel
85b214ea1f Fixed issue #617 - Prevent attachment preview in browser attachment download. 2017-02-02 08:22:33 +01:00
Martin Edenhofer
26dba04436 Fixed issue #659 - Can't update translations via translation table in admin interface. 2017-02-01 23:25:15 +01:00
Martin Edenhofer
4bdd379a05 Fixed issue #686 - Follow up detection not working if ticket_hook_position "none" is used. 2017-01-30 16:25:01 +01:00
Martin Edenhofer
0d82236e7b Updated CHANGELING. 2017-01-16 16:34:04 +01:00
Martin Edenhofer
e68b237c0b Fixed issue #576 - New ticket attributes are not shown for customer. 2017-01-05 09:50:21 +01:00
Martin Edenhofer
b6138d2b47 Fixed unit tests. 2017-01-02 12:20:46 +01:00
Martin Edenhofer
677ee5c547 Rework, get unit tests up and running again. 2017-01-02 11:38:01 +01:00
Martin Edenhofer
b46826145c Fixed issue #566 - Agent - Group assignment shows "<br>”. 2016-12-21 14:12:44 +01:00
Martin Edenhofer
cb1bac8920 Fixed issue #499 - Improved error handling. Fixed Ticket::Overviews.all - use permissions not, not longer role names to decide if user is agent or customer. 2016-12-21 08:51:27 +01:00
Martin Edenhofer
63ed48ae20 Fixed issue #554 - Overview is not show it organization is used as row and first ticket has no organization (app is not responding anymore). 2016-12-16 15:49:24 +01:00
Martin Edenhofer
99753b22cb Fixed issue #169 - Allow users to delete "Notes”. 2016-12-15 17:16:24 +01:00
Martin Edenhofer
5eca4ca6a5 Fixed issue #547 - Layout broken if outbound channel has a error message. 2016-12-15 17:16:14 +01:00
Martin Edenhofer
bd63f10443 Fixed issue #545 - Error message at creation of new overview with not existing specific customer. 2016-12-15 11:39:11 +01:00
Martin Edenhofer
da760de674 Updated. 2016-12-14 16:58:15 +01:00
Martin Edenhofer
37184d6d31 Fixed issue #206 - Wrong recipient on reply to Received call. 2016-12-14 16:07:01 +01:00
Martin Edenhofer
df8100679d Fixed issue #249 - New users won't get initial user group. 2016-12-14 16:06:51 +01:00
Martin Edenhofer
aca15ad056 Improved join. 2016-12-14 03:28:26 +01:00
Timo Göllner
7a39ddb0e5 bugfix: added missing join table (#541) 2016-12-14 03:28:26 +01:00
Martin Edenhofer
7c5742ab23 Fixed issue #433 - Object manager allows method names as attributes. 2016-12-14 01:19:01 +01:00
Martin Edenhofer
2aeace8e59 Fixed issue #418 - Max times for escalation 99 hours. 2016-12-14 00:33:47 +01:00
Martin Edenhofer
7693fefc4d Fixed issue #329 - Pending event time after 22:59 jumps back to 22:59. 2016-12-14 00:33:38 +01:00
Martin Edenhofer
5956d4ab28 Fixed issue#502 - "Get latest Translations" fails(?) without error. 2016-12-06 12:14:15 +01:00
Martin Edenhofer
748515b175 Fixed issue#478 - Token without expiration timestamp expires "01/01/1970”. 2016-11-29 21:55:47 +01:00
Martin Edenhofer
e56aff3e01 Fixed issue#468 - ”Password has been changed" email contains unresolved placeholders. 2016-11-28 15:55:43 +01:00
Martin Edenhofer
f6678e7deb Fixed issue#466 - Search result is not shown if you search for "AAA", "BBB" and "AAA" again. 2016-11-25 14:21:49 +01:00
Martin Edenhofer
9f16d7e557 Fixed issue#402 - Missing quotation marks on sender address. 2016-11-25 13:52:32 +01:00
Martin Edenhofer
40051c6088 Fixed issue#448 - New overview with article conditions not working. 2016-11-21 17:11:22 +01:00
Martin Edenhofer
29ef36e75a Added ES 5 support (filtered query has been deprecated and removed in ES 5.0). 2016-11-19 16:36:30 +01:00
Martin Edenhofer
0f93f01504 Applied new coffeelint. 2016-11-19 16:26:19 +01:00
Martin Edenhofer
50499d667d Fixed issue#437 - Customer should not have the internal attribute to set in customer interface. 2016-11-19 16:26:10 +01:00
Martin Edenhofer
e3ab97b46a Fixed ui postmaster element test. 2016-11-16 23:19:52 +01:00
Martin Edenhofer
98bc61591c Fixed issue#419 - Unable to set owner + customer in postmaster/email filter. 2016-11-16 15:37:35 +01:00
Martin Edenhofer
884e6436b7 Fixed issue#413 - Can't process email / Error creating ticket with changed Ticket Number Format. 2016-11-16 12:45:03 +01:00
Martin Edenhofer
767c8375b2 Fixed issue#408 - "Ticket will escalate soon" in online notifications is not translated. 2016-11-14 13:00:09 +01:00
Martin Edenhofer
a10edc3eda Added change log. 2016-11-14 09:01:04 +01:00
Martin Edenhofer
1b42a90233 Merge branch 'private-issue301' into stable-1.0 2016-11-14 01:03:28 +01:00
Martin Edenhofer
44cd11583c Merge branch 'develop' into stable-1.0 2016-11-12 16:24:57 +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
84 changed files with 2114 additions and 642 deletions

85
CHANGELOG.md Normal file
View File

@@ -0,0 +1,85 @@
# Change Log
## [1.0.4](https://github.com/zammad/zammad/tree/1.0.4) (2017-02-15)
[Full Changelog](https://github.com/zammad/zammad/compare/1.0.3...1.0.4)
**Fixed bugs:**
- Ticket without Subject is blocked - can't do anything with it \("Title needed"\) [\#719](https://github.com/zammad/zammad/issues/719) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Unable to process emails with email addresses longer then 140 signs. [\#650](https://github.com/zammad/zammad/issues/650) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Unable to process emails if "Additional follow-up detection" was checked and unchecked again [\#740](https://github.com/zammad/zammad/issues/740) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Prevent attachment preview in browser attachment download [\#617](https://github.com/zammad/zammad/issues/617) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Follow up detection not working if ticket\_hook\_position "none" is used. [\#686](https://github.com/zammad/zammad/issues/686) [[bug](https://github.com/zammad/zammad/labels/bug)]
## [1.0.3](https://github.com/zammad/zammad/tree/1.0.3) (2017-01-16)
[Full Changelog](https://github.com/zammad/zammad/compare/1.0.2...1.0.3)
**Fixed bugs:**
- Imap sort errors with gmail in production.log [\#568](https://github.com/zammad/zammad/issues/568) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Agent - Group assignment shows "\<br\>" [\#566](https://github.com/zammad/zammad/issues/566) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Overview is not show it organization is used as row and first ticket has no organization \(app is not responding anymore\) [\#554](https://github.com/zammad/zammad/issues/554) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Error message at creation of new overview with not existing specific customer [\#545](https://github.com/zammad/zammad/issues/545) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Renaming standard roles breaks ticket views [\#499](https://github.com/zammad/zammad/issues/499) [[bug](https://github.com/zammad/zammad/labels/bug)]
- New users won't get initial user group [\#249](https://github.com/zammad/zammad/issues/249) [[bug](https://github.com/zammad/zammad/labels/bug)]
- add e.g. screenshots form clipboard doesn't work \(FireFox 45.3.0\) [\#235](https://github.com/zammad/zammad/issues/235) [[bug](https://github.com/zammad/zammad/labels/bug)]
## [1.0.2](https://github.com/zammad/zammad/tree/1.0.2) (2016-12-14)
[Full Changelog](https://github.com/zammad/zammad/compare/1.0.1...1.0.2)
**Implemented enhancements:**
- Provide a Debian Package [\#209](https://github.com/zammad/zammad/issues/209) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
**Fixed bugs:**
- Wrong recipient on reply to Received call [\#206](https://github.com/zammad/zammad/issues/206) [[bug](https://github.com/zammad/zammad/labels/bug)]
- New users won't get initial user group [\#249](https://github.com/zammad/zammad/issues/249) [[bug](https://github.com/zammad/zammad/labels/bug)]
- bugfix: added missing join table [\#541](https://github.com/zammad/zammad/pull/541) [[bug](https://github.com/zammad/zammad/labels/bug)] ([TeaMoe](https://github.com/TeaMoe))
- Object manager allows method names as attributes [\#433](https://github.com/zammad/zammad/issues/433) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Max times for escalation 99 hours [\#418](https://github.com/zammad/zammad/issues/418) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Pending event time after 22:59 jumps back to 22:59 [\#329](https://github.com/zammad/zammad/issues/329) [[bug](https://github.com/zammad/zammad/labels/bug)]
- "Get latest Translations" fails\(?\) without error [\#502](https://github.com/zammad/zammad/issues/502) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Token without expiration timestamp expires "01/01/1970" [\#478](https://github.com/zammad/zammad/issues/478) [[bug](https://github.com/zammad/zammad/labels/bug)]
- "Password has been changed" email contains unresolved placeholders [\#468](https://github.com/zammad/zammad/issues/468) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Search result won't get updated after focus is lost and new search is started [\#466](https://github.com/zammad/zammad/issues/466) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Missing quotation marks on sender address [\#402](https://github.com/zammad/zammad/issues/402) [[bug](https://github.com/zammad/zammad/labels/bug)]
- New overview with article conditions not working [\#448](https://github.com/zammad/zammad/issues/448) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Customer should not have the internal attribute to set in customer interface. [\#437](https://github.com/zammad/zammad/issues/437) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Unable to set owner + customer in postmaster/email filter [\#419](https://github.com/zammad/zammad/issues/419) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Can't process email / Error creating ticket with changed Ticket Number Format [\#413](https://github.com/zammad/zammad/issues/413) [[bug](https://github.com/zammad/zammad/labels/bug)]
- "Ticket will escalate soon" in online notifications is not translated. [\#408](https://github.com/zammad/zammad/issues/408) [[bug](https://github.com/zammad/zammad/labels/bug)]
## [1.0.1](https://github.com/zammad/zammad/tree/1.0.1) (2016-11-14)
[Full Changelog](https://github.com/zammad/zammad/compare/1.0.0...1.0.1)
**Fixed bugs:**
- Some windows in the config area too wide in firefox [\#392](https://github.com/zammad/zammad/issues/392) [[bug](https://github.com/zammad/zammad/labels/bug)]
- \[Critical\] Short display of "Setup a new system" on public site while restarting Zammad! [\#389](https://github.com/zammad/zammad/issues/389) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Mailresponse to customer not working at all after editing a "Trigger" event text [\#385](https://github.com/zammad/zammad/issues/385) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Click on "Reports" leads to Error 500 [\#384](https://github.com/zammad/zammad/issues/384) [[bug](https://github.com/zammad/zammad/labels/bug)]
- TicketSelector in Overview or SLA not working with filter for ticket customer or owner [\#383](https://github.com/zammad/zammad/issues/383) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Changelog generator breaks packager.io builds [\#379](https://github.com/zammad/zammad/issues/379) [[bug](https://github.com/zammad/zammad/labels/bug)]
- script/scheduler.rb crashes after recreation of Zammad database [\#374](https://github.com/zammad/zammad/issues/374) [[bug](https://github.com/zammad/zammad/labels/bug)]
- ticket search cache issue [\#349](https://github.com/zammad/zammad/issues/349) [[bug](https://github.com/zammad/zammad/labels/bug)]
- IMAP mail fetching stops because of broken spam email \(invalid Content-Transfer-Encoding header\) [\#348](https://github.com/zammad/zammad/issues/348) [[bug](https://github.com/zammad/zammad/labels/bug)]
- IMAP mail fetching stops working on spam mail and invalid email format [\#345](https://github.com/zammad/zammad/issues/345) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Unable to create tickets [\#323](https://github.com/zammad/zammad/issues/323) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Admin -\> Channels -\> Email -\> Filters not shown / no name shown [\#313](https://github.com/zammad/zammad/issues/313) [[bug](https://github.com/zammad/zammad/labels/bug)]
- In object manager, blank line in object deleted all values for selections [\#312](https://github.com/zammad/zammad/issues/312) [[bug](https://github.com/zammad/zammad/labels/bug)]
- triggers do not work? [\#306](https://github.com/zammad/zammad/issues/306) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Answers from google mail shows complete quote [\#286](https://github.com/zammad/zammad/issues/286) [[minor bug](https://github.com/zammad/zammad/labels/minor%20bug)]
- Avatar in Chat not showing - wrong url [\#275](https://github.com/zammad/zammad/issues/275) [[bug](https://github.com/zammad/zammad/labels/bug)]
- chat not working / http 404 [\#274](https://github.com/zammad/zammad/issues/274) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Zammad Docker container for rpi/ARM [\#271](https://github.com/zammad/zammad/issues/271) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Zendesk: unknown article type fails import. [\#270](https://github.com/zammad/zammad/issues/270) [[bug](https://github.com/zammad/zammad/labels/bug)]
- No space at comment on ticket bulk action [\#268](https://github.com/zammad/zammad/issues/268) [[bug](https://github.com/zammad/zammad/labels/bug)]
- After adding a new attribute - zammad won't start [\#248](https://github.com/zammad/zammad/issues/248) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Images/Avatars blank [\#217](https://github.com/zammad/zammad/issues/217) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Docker/Ubuntu Not starting postgresql-9.6 service [\#213](https://github.com/zammad/zammad/issues/213) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Can´t choose Organization for Customer [\#211](https://github.com/zammad/zammad/issues/211) [[bug](https://github.com/zammad/zammad/labels/bug)]
- CentOS 7 HTTPS Error 404 - Not Found [\#210](https://github.com/zammad/zammad/issues/210) [[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)*

View File

@@ -277,7 +277,7 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.8.1)
ruby_dep (1.4.0)
rubyzip (1.2.0)
rubyzip (1.2.1)
sass (3.4.22)
sass-rails (5.0.6)
railties (>= 4.0.0, < 6)

View File

@@ -11,7 +11,6 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
updateDataMap = (localParams, localAttribute, localAttributes, localClassname, localForm, localA) =>
localItem = localForm.closest('.js-data')
console.log('updateDataMap', attribute, params)
element = $(App.view("object_manager/attribute/#{localParams.data_type}")(
attribute: attribute
params: params
@@ -49,7 +48,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
objects =
Ticket:
Customer:
create:
create_bottom:
shown: true
required: false
edit:

View File

@@ -16,8 +16,11 @@ class App.UiElement.postmaster_set
relation: 'TicketState'
},
{
value: 'x-zammad-ticket-customer'
value: 'x-zammad-ticket-customer_id'
name: 'Customer'
relation: 'User'
tag: 'user_autocompletion'
disableCreateUser: true,
},
{
@@ -26,10 +29,11 @@ class App.UiElement.postmaster_set
relation: 'Group'
},
{
value: 'x-zammad-ticket-owner'
value: 'x-zammad-ticket-owner_id'
name: 'Owner'
relation: 'User',
tag: 'user_autocompletion',
relation: 'User'
tag: 'user_autocompletion'
disableCreateUser: true,
},
{
value: 'x-zammad-ignore'

View File

@@ -368,9 +368,11 @@ class App.UiElement.ticket_selector
if config.relation is 'User'
config.multiple = false
config.nulloption = false
config.guess = false
if config.relation is 'Organization'
config.multiple = false
config.nulloption = false
config.guess = false
if config.tag is 'checkbox'
config.tag = 'select'
tagSearch = "#{config.tag}_search"

View File

@@ -102,12 +102,20 @@ class App.UiElement.user_permission
if !checked
if rolesWithGroupPlugin[role_id] is 'group'
item.find('.js-groupList').addClass('hidden')
# select groups if only one is available
if hideGroups
item.find('.js-groupList [name=group_ids]').prop('checked', false)
return
# if role with groups plugin is selected, show group selection
if rolesWithGroupPlugin[role_id] is 'group'
item.find('.js-groupList:not(.js-groupListHide)').removeClass('hidden')
# select groups if only one is available
if hideGroups
item.find('.js-groupList [name=group_ids]').prop('checked', true)
for trigger in triggers
trigger.trigger('change')
)

View File

@@ -68,17 +68,18 @@ class Index extends App.ControllerContent
params: defaults
noFieldset: true
)
#new App.ControllerForm(
# el: @el.find('.ticket-form-bottom')
# form_id: @form_id
# model: App.Ticket
# screen: 'create_bottom'#@article_attributes['screen']
# handlers: [
# formChanges
# ]
# filter: @formMeta.filter
# params: defaults
#)
if !_.isEmpty(App.Ticket.attributesGet('create_bottom', false, true))
new App.ControllerForm(
el: @el.find('.ticket-form-bottom')
form_id: @form_id
model: App.Ticket
screen: 'create_bottom'
handlers: [
@ticketFormChanges
]
filter: @formMeta.filter
params: defaults
)
new App.ControllerDrox(
el: @el.find('.sidebar')

View File

@@ -424,11 +424,13 @@ class Table extends App.Controller
attribute.link = object.uiUrl()
value
callbackUserPopover = (value, object, attribute, attributes, refObject) ->
return value if !refObject
attribute.class = 'user-popover'
attribute.data =
id: refObject.id
value
callbackOrganizationPopover = (value, object, attribute, attributes, refObject) ->
return value if !refObject
attribute.class = 'organization-popover'
attribute.data =
id: refObject.id
@@ -716,6 +718,11 @@ class BulkForm extends App.Controller
return
ticket.load(ticket_update)
# if title is empty - ticket can't processed, set ?
if _.isEmpty(ticket.title)
ticket.title = '-'
ticket.save(
done: (r) =>
@bulk_count_index++

View File

@@ -698,11 +698,9 @@ class App.TicketZoom extends App.Controller
if !ticket['owner_id']
ticket['owner_id'] = 1
# check if title exists
if !ticket['title']
alert( App.i18n.translateContent('Title needed') )
@formEnable(e)
return
# if title is empty - ticket can't processed, set ?
if _.isEmpty(ticket.title)
ticket.title = '-'
# stop autosave
@autosaveStop()

View File

@@ -123,6 +123,13 @@ class App.TicketZoomArticleActions extends App.Controller
icon: 'reply'
href: '#'
}
if article.sender.name is 'Agent' && article.type.name is 'phone'
actions.push {
name: 'reply'
type: 'emailReply'
icon: 'reply'
href: '#'
}
if article.type.name is 'twitter status'
actions.push {
name: 'reply'
@@ -284,6 +291,7 @@ class App.TicketZoomArticleActions extends App.Controller
# 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)
@@ -304,15 +312,31 @@ class App.TicketZoomArticleActions extends App.Controller
if type.name is 'email' || type.name is 'phone' || type.name is 'web'
if article.sender.name is 'Agent'
articleNew.to = article.to
else
articleNew.to = article.from
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 = article.created_by.email
articleNew.to = ticket.customer.email
else
if article.sender.name is 'Agent'
articleNew.to = article.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 = {}

View File

@@ -48,6 +48,7 @@ class App.TicketZoomArticleNew extends App.Controller
possibleArticleType['email'] = true
# gets referenced in @setArticleType
@internalSelector = true
@type = @defaults['type'] || 'note'
@articleTypes = []
if possibleArticleType.note
@@ -115,6 +116,9 @@ class App.TicketZoomArticleNew extends App.Controller
},
]
if @permissionCheck('ticket.customer')
@internalSelector = false
@textareaHeight =
open: 148
closed: 20
@@ -194,11 +198,12 @@ class App.TicketZoomArticleNew extends App.Controller
ticket = App.Ticket.fullLocal(@ticket_id)
@html App.view('ticket_zoom/article_new')(
ticket: ticket
articleTypes: @articleTypes
article: @defaults
form_id: @form_id
isCustomer: @permissionCheck('ticket.customer')
ticket: ticket
articleTypes: @articleTypes
article: @defaults
form_id: @form_id
isCustomer: @permissionCheck('ticket.customer')
internalSelector: @internalSelector
)
@setArticleType(@type)

View File

@@ -34,7 +34,7 @@ class App.TicketZoomAttributeBar extends App.Controller
if @resetButton.get(0) && !@resetButton.hasClass('hide')
resetButtonShown = true
macros = App.Macro.all()
macros = App.Macro.findAllByAttribute('active', true)
@macroLastUpdated = App.Macro.lastUpdatedAt()
if _.isEmpty(macros) || !@permissionCheck('ticket.agent')

View File

@@ -164,14 +164,16 @@ class Index extends App.ControllerSubContent
type: 'GET'
url: "#{@apiPath}/translations/sync/#{locale.locale}"
processData: false
complete: (data, status, xhr) =>
success: =>
loader.update(locale.name, false)
locale = locales.shift()
if _.isEmpty(locales)
hide()
return
@_syncChanges(locale, locales, loader, hide)
)
error: ->
hide()
)
updateOnServer: (params, event) =>

View File

@@ -16,11 +16,13 @@ class App.TicketList extends App.Controller
attribute.link = object.uiUrl()
value
callbackUserPopover = (value, object, attribute, attributes, refObject) ->
return value if !refObject
attribute.class = 'user-popover'
attribute.data =
id: refObject.id
value
callbackOrganizationPopover = (value, object, attribute, attributes, refObject) ->
return value if !refObject
attribute.class = 'organization-popover'
attribute.data =
id: refObject.id

View File

@@ -9,7 +9,8 @@ class App.GlobalSearch
class _globalSearchSingleton extends Spine.Module
constructor: ->
@searchResultCache = {}
@searchResultCache = undefined
@searchResultCacheByKey = {}
@apiPath = App.Config.get('api_path')
execute: (params) ->
@@ -20,8 +21,8 @@ class _globalSearchSingleton extends Spine.Module
# use cache for search result
currentTime = new Date
if @searchResultCache[cacheKey] && @searchResultCache[cacheKey].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
render(@searchResultCache[cacheKey].result)
if @searchResultCacheByKey[cacheKey] && @searchResultCacheByKey[cacheKey].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
@renderTry(render, @searchResultCacheByKey[cacheKey].result, cacheKey)
return
App.Ajax.request(
@@ -31,7 +32,7 @@ class _globalSearchSingleton extends Spine.Module
data:
query: query
limit: limit
processData: true,
processData: true
success: (data, status, xhr) =>
App.Collection.loadAssets(data.assets)
result = {}
@@ -48,19 +49,21 @@ class _globalSearchSingleton extends Spine.Module
else
App.Log.error('_globalSearchSingleton', "No such model App.#{item.type}")
diff = false
if @searchResultCache[cacheKey]
diff = difference(@searchResultCache[cacheKey].resultRaw, data.result)
# cache search result
@searchResultCache[cacheKey] =
result: result
resultRaw: data.result
limit: limit
time: new Date
# if result hasn't changed, do not rerender
return if diff isnt false && _.isEmpty(diff)
render(result)
@renderTry(render, result, cacheKey)
)
renderTry: (render, result, cacheKey) =>
# if result hasn't changed, do not rerender
diff = false
if @searchResultCache
diff = difference(@searchResultCache, result)
return if diff isnt false && _.isEmpty(diff)
# cache search result
@searchResultCache = result
@searchResultCacheByKey[cacheKey] =
result: result
time: new Date
render(result)

View File

@@ -282,15 +282,20 @@ class _i18nSingleton extends Spine.Module
notTranslated
removeNotTranslated: (locale, key) =>
return if !@_notTranslated[locale]
delete @_notTranslated[locale][key]
setNotTranslated: (locale, key) =>
if !@_notTranslated[locale]
@_notTranslated[locale] = {}
@_notTranslated[locale][key] = true
date: (time, offset) =>
return time if !time
@convert(time, offset, @mapTime['date'] || @dateFormat)
timestamp: (time, offset) =>
return time if !time
@convert(time, offset, @mapTime['timestamp'] || @timestampFormat)
convert: (time, offset, format) ->

View File

@@ -206,7 +206,7 @@ class App.Utils
# Replace all x tags with the type of replacementTag
html.find('textarea').each( ->
outer = @outerHTML;
outer = @outerHTML
# Replace opening tag
regex = new RegExp('<' + @tagName, 'i')

View File

@@ -485,13 +485,21 @@
},
highlightHour: function() {
var time = this.$element.val(),
timeArray = time.replace(/[^0-9\:]/g, '').split(':'),
hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString(),
hour_max_length = hour.toString().length
this.highlightedUnit = 'hour';
this.setSelectionRange(0, 2);
this.setSelectionRange(0, hour_max_length);
},
highlightMinute: function() {
var time = this.$element.val(),
timeArray = time.replace(/[^0-9\:]/g, '').split(':'),
hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString(),
hour_max_length = hour.toString().length
this.highlightedUnit = 'minute';
this.setSelectionRange(3, 5);
this.setSelectionRange(hour_max_length+1, hour_max_length+3);
},
highlightSecond: function() {
@@ -790,7 +798,8 @@
hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString();
if(this.explicitMode && hour.length > 2 && (hour.length % 2) !== 0 ) {
var hour_max_length = this.maxHours.toString().length
if(this.explicitMode && hour.length > hour_max_length && (hour.length % 2) !== 0 ) {
this.clear();
return;
}
@@ -804,9 +813,9 @@
hour = hour.slice(0, -2);
}
if (hour.length > 2) {
minute = hour.slice(-2);
hour = hour.slice(0, -2);
if (hour.length > hour_max_length) {
minute = hour.slice(-hour_max_length);
hour = hour.slice(0, -hour_max_length);
}
if (minute.length > 2) {
@@ -838,9 +847,9 @@
minute = 59;
}
if (hour >= this.maxHours) {
if (hour > this.maxHours) {
// No day/date handling.
hour = this.maxHours - 1;
hour = this.maxHours;
}
if (this.showMeridian) {
@@ -859,7 +868,7 @@
} else if (hour < 12 && timeMode === 2) {
hour += 12;
} else {
if (hour >= this.maxHours) {
if (hour > this.maxHours) {
hour = this.maxHours - 1;
} else if ((hour < 0) || (hour === 12 && timeMode === 1)){
hour = 0;

View File

@@ -184,7 +184,7 @@ class App.Model extends Spine.Model
###
@attributesGet: (screen = undefined, attributes = false) ->
@attributesGet: (screen = undefined, attributes = false, noDefaultAttributes = false) ->
if !attributes
attributes = clone(App[ @.className ].configure_attributes, true)
else
@@ -203,7 +203,7 @@ class App.Model extends Spine.Model
attributesNew[ attribute.name ] = attribute
# if no screen is given or no attribute has this screen - use default attributes
if !screen || _.isEmpty(attributesNew)
if (!screen || _.isEmpty(attributesNew)) && !noDefaultAttributes
for attribute in attributes
attributesNew[ attribute.name ] = attribute

View File

@@ -89,4 +89,6 @@ class App.Ticket extends App.Model
return App.i18n.translateContent('Pending reminder reached for Ticket |%s|', item.title)
else if item.type is 'escalation'
return App.i18n.translateContent('Ticket |%s| is escalated!', item.title)
else if item.type is 'escalation_warning'
return App.i18n.translateContent('Ticket |%s| will escalate soon!', item.title)
return "Unknow action for (#{@objectDisplayName()}/#{item.type}), extend activityMessage() of model."

View File

@@ -108,7 +108,7 @@
<% if !_.isEmpty(channel.last_log_out): %>
<div class="alert alert--danger">
<%= channel.last_log_out %>
<div>
</div>
<% end %>
<hr>

View File

@@ -3,7 +3,7 @@
<div class="newTicket">
<div class="box box--newTicket">
<div class="page-header">
<h1><%- @T( 'New Ticket' ) %></h1>
<h1><%- @T('New Ticket') %></h1>
</div>
<div class="page-content">
@@ -18,7 +18,6 @@
<div class="ticket-form-bottom"></div>
</div>
<div class="form-controls">
<a class="btn btn--text btn--subtle js-cancel" href="#/"><%- @T( 'Cancel & Go Back' ) %></a>
<button type="submit" class="btn btn--success js-submit align-right"><%- @T( 'Create' ) %></button>

View File

@@ -15,7 +15,7 @@
<input type="checkbox" value="<%= group.id %>" name="group_ids" <% if @groupsSelected[group.id]: %>checked<% end %>/>
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
<span class="label-text"><%= group.displayName() %> <% if group.note: %>- <span class="help-text"><%= group.note %></span><% end %></span>
<span class="label-text"><%= group.displayName() %> <% if group.note: %>- <span class="help-text"><%- group.note %></span><% end %></span>
</label>
<% end %>
</div>

View File

@@ -21,6 +21,7 @@
<% end %>
</div>
</div>
<% if @internalSelector: %>
<div class="editControls-item is-hidden js-toggleVisibility js-selectInternalPublic">
<div class="editControls-iconHolder">
<div class="editControls-icon icon-internal" title="<%- @Ti("set to public") %>">
@@ -31,6 +32,7 @@
</div>
</div>
</div>
<% end %>
</div>
<div class="article-content bubble-gap">
<div class="internal-border">

View File

@@ -51,7 +51,7 @@
<div class="attachments-title"><%- @article.attachments.length %> <%- @T('Attached Files') %></div>
<% for attachment in @article.attachments: %>
<div class="attachment">
<a class="attachment-name u-highlight" href="<%= App.Config.get('api_path') %>/ticket_attachment/<%= @article.ticket_id %>/<%= @article.id %>/<%= attachment.id %>" target="_blank" data-type="attachment"><%= attachment.filename %></a>
<a class="attachment-name u-highlight" href="<%= App.Config.get('api_path') %>/ticket_attachment/<%= @article.ticket_id %>/<%= @article.id %>/<%= attachment.id %>?disposition=attachment" target="_blank" data-type="attachment"><%= attachment.filename %></a>
<div class="attachment-size"><%- @humanFileSize(attachment.size) %></div>
</div>
<% end %>

View File

@@ -1594,7 +1594,6 @@ input[type=time] {
input.timeframe,
input.time {
width: 4.5ch;
box-sizing: content-box;
padding: 0 6px;
line-height: 42px;
@@ -1605,6 +1604,13 @@ input.time {
}
}
input.timeframe {
width: 5.5ch;
}
input.time {
width: 4.5ch;
}
input.time.time--12 {
width: 7.5ch;
}

View File

@@ -239,11 +239,14 @@ class TicketArticlesController < ApplicationController
# find file
file = Store.find(params[:id])
disposition = sanitized_disposition
send_data(
file.content,
filename: file.filename,
type: file.preferences['Content-Type'] || file.preferences['Mime-Type'],
disposition: 'inline'
disposition: disposition
)
end
@@ -271,4 +274,12 @@ class TicketArticlesController < ApplicationController
)
end
private
def sanitized_disposition
disposition = params.fetch(:disposition, 'inline')
valid_disposition = %w(inline attachment)
return disposition if valid_disposition.include?(disposition)
raise Exceptions::NotAuthorized, "Invalid disposition #{disposition} requested. Only #{valid_disposition.join(', ')} are valid."
end
end

View File

@@ -50,11 +50,7 @@ add a new activity entry for an object
# resturn if old entry is really fresh
if result
activity_record_delay = if ENV['ZAMMAD_ACTIVITY_RECORD_DELAY']
ENV['ZAMMAD_ACTIVITY_RECORD_DELAY'].to_i.seconds
else
90.seconds
end
activity_record_delay = 90.seconds
return result if result.created_at.to_i >= ( data[:created_at].to_i - activity_record_delay )
end
@@ -97,13 +93,10 @@ return all activity entries of an user
=end
def self.list(user, limit)
# do not return an activity stream for custoers
# do not return an activity stream for customers
return [] if !user.permissions?('ticket.agent') && !user.permissions?('admin')
permission_ids = []
user.roles.each { |role|
permission_ids = permission_ids.concat(role.permission_ids)
}
permission_ids = user.permissions_with_child_ids
group_ids = user.group_ids
stream = if group_ids.empty?

View File

@@ -201,6 +201,9 @@ returns
next if !respond_to?(real_ids)
attributes[real_ids] = send(real_ids)
}
filter_attributes(attributes)
Cache.write(key, attributes)
attributes
end
@@ -260,12 +263,16 @@ returns
attributes[destination] = user.login
}
filter_attributes(attributes)
attributes
end
def filter_attributes(attributes)
# remove forbitten attributes
%w(password token tokens token_ids).each { |item|
attributes.delete(item)
}
attributes
end
=begin

View File

@@ -126,6 +126,21 @@ module Channel::EmailBuild
mail
end
=begin
quoted_in_one_line = Channel::EmailBuild.recipient_line('Somebody @ "Company"', 'some.body@example.com')
returnes
'"Somebody @ \"Company\"" <some.body@example.com>'
=end
def self.recipient_line(realname, email)
return "#{realname} <#{email}>" if realname =~ /^[A-z]+$/i
"\"#{realname.gsub('"', '\"')}\" <#{email}>"
end
=begin
Check if string is a complete html document. If not, add head and css styles.

View File

@@ -81,14 +81,14 @@ class Channel::EmailParser
data[field.name.to_s.downcase.to_sym] = Encode.conv('utf8', field.to_s)
# if we need to access the lines by objects later again
data[ "raw-#{field.name.downcase}".to_sym ] = field
data["raw-#{field.name.downcase}".to_sym] = field
}
# get sender
from = nil
['from', 'reply-to', 'return-path'].each { |item|
next if !mail[ item.to_sym ]
from = mail[ item.to_sym ].value
next if !mail[item.to_sym]
from = mail[item.to_sym].value
break if from
}
@@ -371,7 +371,7 @@ returns
do not raise an exception - e. g. if used by scheduler
parser = Channel::EmailParser.new
ticket, article, user = parser.process(channel, email_raw_string, fakse)
ticket, article, user = parser.process(channel, email_raw_string, false)
returns
@@ -424,7 +424,7 @@ returns
}
# check ignore header
if mail[ 'x-zammad-ignore'.to_sym ] == 'true' || mail[ 'x-zammad-ignore'.to_sym ] == true
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
end
@@ -440,7 +440,7 @@ returns
Transaction.execute(interface_handle: "#{original_interface_handle}.postmaster") do
# get sender user
session_user_id = mail[ 'x-zammad-session-user-id'.to_sym ]
session_user_id = mail['x-zammad-session-user-id'.to_sym]
if !session_user_id
raise 'No x-zammad-session-user-id, no sender set!'
end
@@ -453,11 +453,11 @@ returns
UserInfo.current_user_id = session_user.id
# get ticket# based on email headers
if mail[ 'x-zammad-ticket-id'.to_sym ]
ticket = Ticket.find_by(id: mail[ 'x-zammad-ticket-id'.to_sym ])
if mail['x-zammad-ticket-id'.to_sym]
ticket = Ticket.find_by(id: mail['x-zammad-ticket-id'.to_sym])
end
if mail[ 'x-zammad-ticket-number'.to_sym ]
ticket = Ticket.find_by(number: mail[ 'x-zammad-ticket-number'.to_sym ])
if mail['x-zammad-ticket-number'.to_sym]
ticket = Ticket.find_by(number: mail['x-zammad-ticket-number'.to_sym])
end
# set ticket state to open if not new
@@ -476,8 +476,8 @@ returns
end
# set ticket to open again
if !mail[ 'x-zammad-ticket-followup-state'.to_sym ]
if state_type.name != 'new' && !mail[ 'x-zammad-out-of-office'.to_sym ]
if !mail['x-zammad-ticket-followup-state'.to_sym] && !mail['x-zammad-ticket-followup-state_id'.to_sym]
if state_type.name != 'new' && !mail['x-zammad-out-of-office'.to_sym]
ticket.state = Ticket::State.find_by(name: 'open')
ticket.save!
end
@@ -601,12 +601,12 @@ returns
header = "x-zammad-#{header_name}-#{suffix}-#{key_short}"
end
if mail[ header.to_sym ]
Rails.logger.info "header #{header} found #{mail[ header.to_sym ]}"
Rails.logger.info "header #{header} found #{mail[header.to_sym]}"
item_object.class.reflect_on_all_associations.map { |assoc|
next if assoc.name.to_s != key_short
Rails.logger.info "ASSOC found #{assoc.class_name} lookup #{mail[ header.to_sym ]}"
Rails.logger.info "ASSOC found #{assoc.class_name} lookup #{mail[header.to_sym]}"
item = assoc.class_name.constantize
if item.respond_to?(:name)
@@ -627,9 +627,9 @@ returns
if suffix
header = "x-zammad-#{header_name}-#{suffix}-#{key}"
end
if mail[ header.to_sym ]
Rails.logger.info "header #{header} found #{mail[ header.to_sym ]}"
item_object[key] = mail[ header.to_sym ]
if mail[header.to_sym]
Rails.logger.info "header #{header} found #{mail[header.to_sym]}"
item_object[key] = mail[header.to_sym]
end
}
end

View File

@@ -3,10 +3,10 @@
# process all database filter
module Channel::Filter::Database
def self.run( _channel, mail )
def self.run(_channel, mail)
# process postmaster filter
filters = PostmasterFilter.where( active: true, channel: 'email' ).order(:name, :created_at)
filters = PostmasterFilter.where(active: true, channel: 'email').order(:name, :created_at)
filters.each { |filter|
Rails.logger.info " process filter #{filter.name} ..."
all_matches_ok = true

View File

@@ -14,7 +14,7 @@ module Channel::Filter::FollowUpCheck
return true
end
setting = Setting.get('postmaster_follow_up_search_in')
setting = Setting.get('postmaster_follow_up_search_in') || []
# get ticket# from body
if setting.include?('body')
@@ -39,7 +39,7 @@ module Channel::Filter::FollowUpCheck
end
# get ticket# from references
if setting.include?('references') || mail[ 'x-zammad-is-auto-response'.to_sym ] == true
if setting.include?('references') || (mail[ 'x-zammad-is-auto-response'.to_sym ] == true || Setting.get('ticket_hook_position') == 'none')
# get all references 'References' + 'In-Reply-To'
references = ''
@@ -66,7 +66,7 @@ module Channel::Filter::FollowUpCheck
end
# get ticket# from references current email has same subject as inital article
if !mail[:subject].empty?
if mail[:subject].present?
# get all references 'References' + 'In-Reply-To'
references = ''

View File

@@ -1,2 +1,5 @@
class Chat::Message < ApplicationModel
include HtmlSanitized
sanitized_html :content
end

View File

@@ -0,0 +1,46 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module HtmlSanitized
extend ActiveSupport::Concern
included do
before_create :sanitized_html_attributes
before_update :sanitized_html_attributes
end
def sanitized_html_attributes
html_attributes = self.class.instance_variable_get(:@sanitized_html) || []
return if html_attributes.empty?
html_attributes.each do |attribute|
value = send(attribute)
next if value.blank?
next if !sanitizeable?(attribute, value)
send("#{attribute}=".to_sym, HtmlSanitizer.strict(value))
end
end
def sanitizeable?(_attribute, _value)
true
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
serve methode to mark HTML attrbibutes that need to get sanitized
class Model < ApplicationModel
include Sanitized
sanitized_html :body
end
=end
def sanitized_html(*attributes)
@sanitized_html = attributes
end
end
end

View File

@@ -663,6 +663,14 @@ to send no browser reload event, pass false
raise 'At least one letters is needed'
elsif name =~ /^(destroy|true|false|integer|select|drop|create|alter|index|table|varchar|blob|date|datetime|timestamp)$/
raise "#{name} is a reserved word, please choose a different one"
# do not allow model method names as attributes
else
model = Kernel.const_get(object_lookup.name)
record = model.new
if record.respond_to?(name.to_sym) && !record.attributes.key?(name)
raise "#{name} is a reserved word, please choose a different one"
end
end
true
end

View File

@@ -46,13 +46,13 @@ class Observer::Ticket::Article::FillupFromEmail < ActiveRecord::Observer
if !email_address
raise "No email address found for group '#{ticket.group.name}'"
end
system_sender = "#{email_address.realname} <#{email_address.email}>"
if record.created_by_id != 1 && Setting.get('ticket_define_email_from') == 'AgentNameSystemAddressName'
seperator = Setting.get('ticket_define_email_from_seperator')
sender = User.find(record.created_by_id)
record.from = "#{sender.firstname} #{sender.lastname} #{seperator} #{system_sender}"
seperator = Setting.get('ticket_define_email_from_seperator')
sender = User.find(record.created_by_id)
realname = "#{sender.firstname} #{sender.lastname} #{seperator} #{email_address.realname}"
record.from = Channel::EmailBuild.recipient_line(realname, email_address.email)
else
record.from = system_sender
record.from = Channel::EmailBuild.recipient_line(email_address.realname, email_address.email)
end
end
end

View File

@@ -30,6 +30,20 @@ class Observer::Ticket::ArticleChanges < ActiveRecord::Observer
record.ticket.save
end
def after_destroy(record)
changed = false
if article_count_update(record)
changed = true
end
# save ticket
if !changed
record.ticket.touch
return
end
record.ticket.save
end
# get article count
def article_count_update(record)
current_count = record.ticket.article_count

View File

@@ -52,7 +52,7 @@ get signup roles
Role.signup_roles
returnes
returns
[role1, role2, ...]
@@ -68,7 +68,7 @@ get signup role ids
Role.signup_role_ids
returnes
returns
[role1, role2, ...]

View File

@@ -1,5 +1,7 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Ticket::Article < ApplicationModel
include HtmlSanitized
load 'ticket/article/assets.rb'
include Ticket::Article::Assets
load 'ticket/article/history_log.rb'
@@ -16,6 +18,8 @@ class Ticket::Article < ApplicationModel
before_create :check_subject, :check_message_id_md5
before_update :check_subject, :check_message_id_md5
sanitized_html :body
notify_clients_support
activity_stream_support(
@@ -164,6 +168,12 @@ get body as text with quote sign "> " at the beginning of each line
body_as_text.word_wrap.message_quote
end
def sanitizeable?(attribute, _value)
return true if attribute != :body
return false if content_type.blank?
content_type =~ /html/i
end
private
# strip not wanted chars

View File

@@ -43,6 +43,14 @@ module Ticket::Number::Date
# vehikel number. The modulus to 10 of this sum is substracted from
# 10. See: http://www.pruefziffernberechnung.de/F/Fahrzeugnummer.shtml
# (german)
# fix for https://github.com/zammad/zammad/issues/413 - can be removed later
if config.class == FalseClass || config.class == TrueClass
config = {
checksum: config
}
end
if config[:checksum]
chksum = 0
mult = 1

View File

@@ -16,32 +16,31 @@ returns
=end
def self.all(data)
current_user = data[:current_user]
# get customer overviews
if data[:current_user].role?('Customer')
role_id = Role.lookup(name: 'Customer').id
overviews = if data[:current_user].organization_id && data[:current_user].organization.shared
Overview.where(role_id: role_id, active: true).order(:prio)
if current_user.permissions?('ticket.customer')
overviews = if current_user.organization_id && current_user.organization.shared
Overview.where(role_id: current_user.role_ids, active: true).order(:prio)
else
Overview.where(role_id: role_id, organization_shared: false, active: true).order(:prio)
Overview.where(role_id: current_user.role_ids, organization_shared: false, active: true).order(:prio)
end
overviews_list = []
overviews.each { |overview|
user_ids = overview.user_ids
next if !user_ids.empty? && !user_ids.include?(data[:current_user].id)
next if !user_ids.empty? && !user_ids.include?(current_user.id)
overviews_list.push overview
}
return overviews_list
end
# get agent overviews
return if !data[:current_user].role?('Agent')
role_id = Role.lookup(name: 'Agent').id
overviews = Overview.where(role_id: role_id, active: true).order(:prio)
return [] if !current_user.permissions?('ticket.agent')
overviews = Overview.where(role_id: current_user.role_ids, active: true).order(:prio)
overviews_list = []
overviews.each { |overview|
user_ids = overview.user_ids
next if !user_ids.empty? && !user_ids.include?(data[:current_user].id)
next if !user_ids.empty? && !user_ids.include?(current_user.id)
overviews_list.push overview
}
overviews_list
@@ -86,17 +85,41 @@ returns
overviews = Ticket::Overviews.all(
current_user: user,
)
return [] if overviews.blank?
# get only tickets with permissions
access_condition = Ticket.access_condition(user)
ticket_attributes = Ticket.new.attributes
list = []
overviews.each { |overview|
query_condition, bind_condition, tables = Ticket.selector2sql(overview.condition, user)
order_by = "#{overview.order[:by]} #{overview.order[:direction]}"
# validate direction
raise "Invalid order direction '#{overview.order[:direction]}'" if overview.order[:direction] && overview.order[:direction] !~ /^(ASC|DESC)$/i
# check if order by exists
order_by = overview.order[:by]
if !ticket_attributes.key?(order_by)
order_by = if ticket_attributes.key?("#{order_by}_id")
"#{order_by}_id"
else
'created_at'
end
end
order_by = "tickets.#{order_by} #{overview.order[:direction]}"
# check if group by exists
if overview.group_by && !overview.group_by.empty?
order_by = "#{overview.group_by}_id, #{order_by}"
group_by = overview.group_by
if !ticket_attributes.key?(group_by)
group_by = if ticket_attributes.key?("#{group_by}_id")
"#{group_by}_id"
end
end
if group_by
order_by = "tickets.#{group_by}, #{order_by}"
end
end
ticket_result = Ticket.select('id, updated_at')
@@ -115,9 +138,10 @@ returns
}
tickets.push ticket_item
}
count = Ticket.where(access_condition).where(query_condition, *bind_condition).count()
count = Ticket.where(access_condition).where(query_condition, *bind_condition).joins(tables).count()
item = {
overview: {
name: overview.name,
id: overview.id,
view: overview.link,
updated_at: overview.updated_at,

View File

@@ -162,8 +162,9 @@ returns
.order('tickets.created_at DESC')
.limit(limit)
else
query_condition, bind_condition = selector2sql(condition)
query_condition, bind_condition, tables = selector2sql(condition)
tickets_all = Ticket.select('DISTINCT(tickets.id), tickets.created_at')
.joins(tables)
.where(access_condition)
.where(query_condition, *bind_condition)
.order('tickets.created_at DESC')

View File

@@ -394,6 +394,32 @@ returns
=begin
returns all accessable permission ids of user
user = User.find(123)
user.permissions_with_child_ids
returns
[permission1_id, permission2_id, permission3_id]
=end
def permissions_with_child_ids
where = ''
where_bind = [true]
permissions.each { |permission_name, _value|
where += ' OR ' if where != ''
where += 'permissions.name = ? OR permissions.name LIKE ?'
where_bind.push permission_name
where_bind.push "#{permission_name}.%"
}
return [] if where == ''
Object.const_get('Permission').where("permissions.active = ? AND (#{where})", *where_bind).pluck(:id)
end
=begin
get all users with permission
users = User.with_permissions('admin.session')

View File

@@ -1,9 +1,9 @@
Your #{product_name} password has been changed
Your #{config.product_name} password has been changed
<p>Hi #{user.firstname},</p>
<br>
<p>The password for your #{product_name} account <b>#{user.login}</b> has been changed recently.</p>
<p>The password for your #{config.product_name} account <b>#{user.login}</b> has been changed recently.</p>
<br>
<p>This activity is not known to you? If not, contact your system administrator.</p>
<br>
<p>Your #{product_name} Team</p>
<p>Your #{config.product_name} Team</p>

View File

@@ -0,0 +1,48 @@
# content of this tags will also be removed
Rails.application.config.html_sanitizer_tags_remove_content = %w(
style
)
# only this tags are allowed
Rails.application.config.html_sanitizer_tags_whitelist = %w(
a abbr acronym address area article aside audio
b bdi bdo big blockquote br
canvas caption center cite code col colgroup command
datalist dd del details dfn dir div dl dt em
figcaption figure footer h1 h2 h3 h4 h5 h6 header hr
i img ins kbd label legend li map mark menu meter nav
ol output optgroup option p pre q
s samp section small span strike strong sub summary sup
text table tbody td tfoot th thead time tr tt u ul var video
)
# attributes allowed for tags
Rails.application.config.html_sanitizer_attributes_whitelist = {
:all => %w(class dir lang style title translate data-signature data-signature-id),
'a' => %w(href hreflang name rel),
'abbr' => %w(title),
'blockquote' => %w(cite),
'col' => %w(span width),
'colgroup' => %w(span width),
'data' => %w(value),
'del' => %w(cite datetime),
'dfn' => %w(title),
'img' => %w(align alt border height src srcset width),
'ins' => %w(cite datetime),
'li' => %w(value),
'ol' => %w(reversed start type),
'table' => %w(align bgcolor border cellpadding cellspacing frame rules sortable summary width),
'td' => %w(abbr align axis colspan headers rowspan valign width),
'th' => %w(abbr align axis colspan headers rowspan scope sorted valign width),
'ul' => %w(type),
'q' => %w(cite),
'time' => %w(datetime pubdate),
}
# only this css properties are allowed
Rails.application.config.html_sanitizer_css_properties_whitelist = %w(
width height
max-width min-width
max-height min-height
)

View File

@@ -16,10 +16,10 @@ class CreateBase < ActiveRecord::Migration
create_table :users do |t|
t.references :organization, null: true
t.string :login, limit: 100, null: false
t.string :login, limit: 255, null: false
t.string :firstname, limit: 100, null: true, default: ''
t.string :lastname, limit: 100, null: true, default: ''
t.string :email, limit: 140, null: true, default: ''
t.string :email, limit: 255, null: true, default: ''
t.string :image, limit: 100, null: true
t.string :image_source, limit: 200, null: true
t.string :web, limit: 100, null: true, default: ''

View File

@@ -0,0 +1,10 @@
class LoginEmailLength650 < ActiveRecord::Migration
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
change_column(:users, :login, :string, limit: 255)
change_column(:users, :email, :string, limit: 255)
end
end

92
lib/html_sanitizer.rb Normal file
View File

@@ -0,0 +1,92 @@
class HtmlSanitizer
def self.strict(string)
# config
tags_remove_content = Rails.configuration.html_sanitizer_tags_remove_content
tags_whitelist = Rails.configuration.html_sanitizer_tags_whitelist
attributes_whitelist = Rails.configuration.html_sanitizer_attributes_whitelist
css_properties_whitelist = Rails.configuration.html_sanitizer_css_properties_whitelist
scrubber = Loofah::Scrubber.new do |node|
# remove tags with subtree
if tags_remove_content.include?(node.name)
node.remove
end
# replace tags, keep subtree
if !tags_whitelist.include?(node.name)
traversal(node, scrubber)
end
# prepare src attribute
if node['src']
src = cleanup(node['src'])
if src =~ /(javascript|livescript|vbscript):/i || src.start_with?('http', 'ftp', '//')
traversal(node, scrubber)
end
end
# clean style / only use allowed style properties
if node['style']
pears = node['style'].downcase.gsub(/\t|\n|\r/, '').split(';')
style = ''
pears.each { |pear|
prop = pear.split(':')
next if !prop[0]
key = prop[0].strip
next if !css_properties_whitelist.include?(key)
style += "#{pear};"
}
node['style'] = style
if style == ''
node.delete('style')
end
end
# scan for invalid link content
%w(href style).each { |attribute_name|
next if !node[attribute_name]
href = cleanup(node[attribute_name])
next if href !~ /(javascript|livescript|vbscript):/i
node.delete(attribute_name)
}
# remove attributes if not whitelisted
node.each { |attribute, _value|
attribute_name = attribute.downcase
next if attributes_whitelist[:all].include?(attribute_name) || (attributes_whitelist[node.name] && attributes_whitelist[node.name].include?(attribute_name))
node.delete(attribute)
}
# prepare links
if node['href']
href = cleanup(node['href'])
next if !href.start_with?('http', 'ftp', '//')
node.set_attribute('rel', 'nofollow')
node.set_attribute('target', '_blank')
end
end
Loofah.fragment(string).scrub!(scrubber).to_s
end
def self.traversal(node, scrubber)
node.children.each { |child|
if child.class == Nokogiri::XML::CDATA
node.before Nokogiri::XML::Text.new(node.content, node.document)
else
node.before Loofah.fragment(child.to_s).scrub!(scrubber)
end
}
node.remove
end
def self.cleanup(string)
string.downcase.gsub(/[[:space:]]|\t|\n|\r/, '').gsub(%r{/\*.*?\*/}, '').gsub(/<!--.*?-->/, '').gsub(/\[.+?\]/, '')
end
private_class_method :traversal
private_class_method :cleanup
end

View File

@@ -32,6 +32,7 @@ returns
next if entry =~ %r{channel/}i
next if entry =~ %r{observer/}i
next if entry =~ %r{store/provider/}i
next if entry =~ %r{models/concerns/}i
entry.gsub!(dir, '')
entry = entry.to_classname
model_class = load_adapter(entry)

View File

@@ -339,8 +339,6 @@ get count of tickets and tickets which match on selector
end
def self.selector2query(selector, _current_user, aggs_interval, limit)
filter_must = []
filter_must_not = []
query_must = []
query_must_not = []
if selector && !selector.empty?
@@ -355,9 +353,9 @@ get count of tickets and tickets which match on selector
t[:term][key_tmp] = data['value']
end
if data['operator'] == 'is'
filter_must.push t
query_must.push t
elsif data['operator'] == 'is not'
filter_must_not.push t
query_must_not.push t
elsif data['operator'] == 'contains'
query_must.push t
elsif data['operator'] == 'contains not'
@@ -391,43 +389,18 @@ get count of tickets and tickets which match on selector
from: aggs_interval[:from],
to: aggs_interval[:to],
}
filter_must.push r
query_must.push r
end
if !query_must.empty? || !query_must_not.empty?
if !data[:query][:filtered]
data[:query][:filtered] = {}
end
if !data[:query][:filtered][:query]
data[:query][:filtered][:query] = {}
end
if !data[:query][:filtered][:query][:bool]
data[:query][:filtered][:query][:bool] = {}
end
if !data[:query][:bool]
data[:query][:bool] = {}
end
if !query_must.empty?
data[:query][:filtered][:query][:bool][:must] = query_must
data[:query][:bool][:must] = query_must
end
if !query_must_not.empty?
data[:query][:filtered][:query][:bool][:must_not] = query_must_not
end
if !filter_must.empty? || !filter_must.empty?
if !data[:query][:filtered]
data[:query][:filtered] = {}
end
if !data[:query][:filtered][:filter]
data[:query][:filtered][:filter] = {}
end
if !data[:query][:filtered][:filter][:bool]
data[:query][:filtered][:filter][:bool] = {}
end
end
if !filter_must.empty?
data[:query][:filtered][:filter][:bool][:must] = filter_must
end
if !filter_must_not.empty?
data[:query][:filtered][:filter][:bool][:must_not] = filter_must_not
data[:query][:bool][:must_not] = query_must_not
end
# add sort

View File

@@ -312,6 +312,27 @@ test('i18n', function() {
var timestamp = App.i18n.translateTimestamp('2012-11-06T21:07:24Z', offset);
equal(timestamp, '06.11.2012 21:07', 'de-de - timestamp translated correctly')
timestamp = App.i18n.translateTimestamp('', offset);
equal(timestamp, '', 'de-de - timestamp translated correctly')
timestamp = App.i18n.translateTimestamp(null, offset);
equal(timestamp, null, 'de-de - timestamp translated correctly')
timestamp = App.i18n.translateTimestamp(undefined, offset);
equal(timestamp, undefined, 'de-de - timestamp translated correctly')
var date = App.i18n.translateDate('2012-11-06', 0)
equal(date, '06.11.2012', 'de-de - date translated correctly')
date = App.i18n.translateDate('', 0)
equal(date, '', 'de-de - date translated correctly')
date = App.i18n.translateDate(null, 0)
equal(date, null, 'de-de - date translated correctly')
date = App.i18n.translateDate(undefined, 0)
equal(date, undefined, 'de-de - date translated correctly')
// en
App.i18n.set('en-us')
translated = App.i18n.translateContent('yes')
@@ -368,6 +389,27 @@ test('i18n', function() {
timestamp = App.i18n.translateTimestamp('2012-11-06T21:07:24Z', offset)
equal(timestamp, '11/06/2012 21:07', 'en - timestamp translated correctly')
timestamp = App.i18n.translateTimestamp('', offset);
equal(timestamp, '', 'en - timestamp translated correctly')
timestamp = App.i18n.translateTimestamp(null, offset);
equal(timestamp, null, 'en - timestamp translated correctly')
timestamp = App.i18n.translateTimestamp(undefined, offset);
equal(timestamp, undefined, 'en - timestamp translated correctly')
date = App.i18n.translateDate('2012-11-06', 0)
equal(date, '11/06/2012', 'en - date translated correctly')
date = App.i18n.translateDate('', 0)
equal(date, '', 'en - date translated correctly')
date = App.i18n.translateDate(null, 0)
equal(date, null, 'en - date translated correctly')
date = App.i18n.translateDate(undefined, 0)
equal(date, undefined, 'en - date translated correctly')
// locale alias test
// de
App.i18n.set('de')

View File

@@ -715,13 +715,14 @@ test("form postmaster filter", function() {
},
},
set: {
'x-zammad-ticket-customer': {
value: 'customer'
'x-zammad-ticket-customer_id': {
value: 'customer',
value_completion: ''
},
'x-zammad-ticket-group_id': {
value: '1'
},
'x-zammad-ticket-owner': {
'x-zammad-ticket-owner_id': {
value: 'owner',
value_completion: ''
},
@@ -757,13 +758,14 @@ test("form postmaster filter", function() {
}
},
set: {
'x-zammad-ticket-customer': {
value: 'customer'
'x-zammad-ticket-customer_id': {
value: 'customer',
value_completion: ''
},
'x-zammad-ticket-group_id': {
value: '1'
},
'x-zammad-ticket-owner': {
'x-zammad-ticket-owner_id': {
value: 'owner',
value_completion: ''
},
@@ -774,7 +776,7 @@ test("form postmaster filter", function() {
};
deepEqual(params, test_params, 'form param check')
el.find('[name="set::x-zammad-ticket-priority_id::value"]').closest('.js-filterElement').find('.js-remove').click()
el.find('[name="set::x-zammad-ticket-customer::value"]').closest('.js-filterElement').find('.js-remove').click()
el.find('[name="set::x-zammad-ticket-customer_id::value"]').closest('.js-filterElement').find('.js-remove').click()
App.Delay.set(function() {
test("form param check after remove click", function() {
params = App.ControllerForm.params(el)
@@ -792,7 +794,7 @@ test("form postmaster filter", function() {
}
},
set: {
'x-zammad-ticket-owner': {
'x-zammad-ticket-owner_id': {
value: 'owner',
value_completion: ''
},

View File

@@ -57,9 +57,72 @@ class AgentTicketActionLevel0Test < TestCase
css: '.modal [name="email"]',
value: "#{agent}@example.com",
)
exists_not(
css: '.modal select[name="group_ids"]',
exists(
displayed: false,
css: '.modal [name="group_ids"]',
)
exists(
css: '.modal [name="group_ids"]:checked',
)
click(
css: '.modal button.btn.btn--primary',
fast: true,
)
watch_for(
css: 'body div.modal',
value: 'Sending',
)
watch_for_disappear(
css: 'body div.modal',
value: 'Sending',
)
click(css: '#navigation a[href="#dashboard"]')
click(css: '.active.content .tab[data-area="first-steps-widgets"]')
watch_for(
css: '.active.content',
value: 'Configuration',
)
click(css: '.active.content .js-inviteAgent')
modal_ready()
set(
css: '.modal [name="firstname"]',
value: 'Bob2',
)
set(
css: '.modal [name="lastname"]',
value: 'Smith2',
)
set(
css: '.modal [name="email"]',
value: "#{agent}2@example.com",
)
# disable agent role
uncheck(
css: '.modal [name="role_ids"][value=2]',
)
exists(
displayed: false,
css: '.modal [name="group_ids"]',
)
exists_not(
css: '.modal [name="group_ids"]:checked',
)
# enable agent role
check(
css: '.modal [name="role_ids"][value=2]',
)
exists(
css: '.modal [name="group_ids"]',
)
exists(
css: '.modal [name="group_ids"]:checked',
)
click(
css: '.modal button.btn.btn--primary',
fast: true,

View File

@@ -28,6 +28,13 @@ class CustomerTicketCreateTest < TestCase
css: '.newTicket [data-name="body"]',
value: 'some body 123äöü',
)
exists_not(
css: '.newTicket input[name="customer_id"]',
)
exists_not(
css: '.newTicket input[name="priority_id"]',
)
click(css: '.newTicket button.js-submit')
sleep 5
@@ -42,7 +49,7 @@ class CustomerTicketCreateTest < TestCase
# update ticket
set(
css: '.active [data-name="body"]',
css: '.content.active [data-name="body"]',
value: 'some body 1234 äöüß',
no_click: true,
)
@@ -51,10 +58,10 @@ class CustomerTicketCreateTest < TestCase
type: 'stayOnTab',
)
click(css: '.active .js-submit')
click(css: '.content.active .js-submit')
watch_for(
css: '.active div.ticket-article',
css: '.content.active div.ticket-article',
value: 'some body 1234 äöüß',
)
end

View File

@@ -2,6 +2,7 @@
require 'browser_test_helper'
class TranslationTest < TestCase
def test_preferences
@browser = browser_instance
login(
@@ -250,4 +251,60 @@ class TranslationTest < TestCase
)
end
def test_admin_sync
@browser = browser_instance
login(
username: 'master@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all()
click(css: 'a[href="#current_user"]')
click(css: 'a[href="#profile"]')
click(css: 'a[href="#profile/language"]')
select(
css: '.language_item [name="locale"]',
value: 'Deutsch',
)
click(css: '.content.active button[type="submit"]')
watch_for(
css: 'body',
value: 'Sprache',
)
click(css: 'a[href="#manage"]')
click(css: 'a[href="#system/translation"]')
watch_for(
css: '.content.active',
value: 'Inline Übersetzung',
)
click(css: '.content.active .js-syncChanges')
watch_for(
css: '.content.active .modal',
value: 'Letzte Übersetzung laden',
)
watch_for_disappear(
css: '.content.active .modal',
timeout: 5 * 60,
)
click(css: 'a[href="#current_user"]')
click(css: 'a[href="#profile"]')
click(css: 'a[href="#profile/language"]')
select(
css: '.language_item [name="locale"]',
value: 'English (United States)',
)
click(css: '.content.active button[type="submit"]')
watch_for(
css: 'body',
value: 'Language',
)
end
end

View File

@@ -547,6 +547,12 @@ class TestCase < Test::Unit::TestCase
css: '.some_class',
)
exists(
displayed: false, # true|false
browser: browser1,
css: '.some_class',
)
=end
def exists(params)
@@ -558,6 +564,16 @@ class TestCase < Test::Unit::TestCase
screenshot(browser: instance, comment: 'exists_failed')
raise "#{params[:css]} dosn't exist, but should"
end
if params.key?(:displayed)
if params[:displayed] == true && !instance.find_elements(css: params[:css])[0].displayed?
raise "#{params[:css]} is not displayed, but should"
end
if params[:displayed] == false && instance.find_elements(css: params[:css])[0].displayed?
raise "#{params[:css]} is displayed, but should not"
end
end
true
end

View File

@@ -2,7 +2,7 @@
require 'test_helper'
class SearchControllerTest < ActionDispatch::IntegrationTest
setup do
def base_data
# clear cache
Cache.clear
@@ -192,6 +192,7 @@ class SearchControllerTest < ActionDispatch::IntegrationTest
end
test 'settings index with nobody' do
base_data
params = {
query: 'test 1234',
@@ -222,6 +223,7 @@ class SearchControllerTest < ActionDispatch::IntegrationTest
end
test 'settings index with admin' do
base_data
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('search-admin@example.com', 'adminpw')
@@ -295,6 +297,7 @@ class SearchControllerTest < ActionDispatch::IntegrationTest
end
test 'settings index with agent' do
base_data
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('search-agent@example.com', 'agentpw')
@@ -368,6 +371,7 @@ class SearchControllerTest < ActionDispatch::IntegrationTest
end
test 'settings index with customer 1' do
base_data
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('search-customer1@example.com', 'customer1pw')
@@ -413,6 +417,7 @@ class SearchControllerTest < ActionDispatch::IntegrationTest
end
test 'settings index with customer 2' do
base_data
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('search-customer2@example.com', 'customer2pw')

View File

@@ -318,7 +318,7 @@ class TicketsControllerTest < ActionDispatch::IntegrationTest
result = JSON.parse(@response.body)
assert_equal(Hash, result.class)
assert_equal(ticket.id, result['ticket_id'])
assert_equal('Tickets Agent via Zammad <zammad@localhost>', result['from'])
assert_equal('"Tickets Agent via Zammad" <zammad@localhost>', result['from'])
assert_equal('some subject', result['subject'])
assert_equal('some body', result['body'])
assert_equal('text/plain', result['content_type'])
@@ -335,7 +335,7 @@ class TicketsControllerTest < ActionDispatch::IntegrationTest
result = JSON.parse(@response.body)
assert_equal(Hash, result.class)
assert_equal(ticket.id, result['ticket_id'])
assert_equal('Tickets Agent via Zammad <zammad@localhost>', result['from'])
assert_equal('"Tickets Agent via Zammad" <zammad@localhost>', result['from'])
assert_equal('new subject', result['subject'])
assert_equal('some body', result['body'])
assert_equal('text/plain', result['content_type'])
@@ -444,7 +444,7 @@ class TicketsControllerTest < ActionDispatch::IntegrationTest
result = JSON.parse(@response.body)
assert_equal(Hash, result.class)
assert_equal(ticket.id, result['ticket_id'])
assert_equal('Tickets Admin via Zammad <zammad@localhost>', result['from'])
assert_equal('"Tickets Admin via Zammad" <zammad@localhost>', result['from'])
assert_equal('some subject', result['subject'])
assert_equal('some body', result['body'])
assert_equal('text/plain', result['content_type'])

View File

@@ -38,6 +38,9 @@ class ActiveSupport::TestCase
# remove background jobs
Delayed::Job.destroy_all
ActivityStream.destroy_all
PostmasterFilter.destroy_all
Ticket.destroy_all
# set current user
UserInfo.current_user_id = nil

View File

@@ -4,9 +4,8 @@ require 'test_helper'
class ActivityStreamTest < ActiveSupport::TestCase
admin_user = nil
current_user = nil
activity_record_delay = nil
test 'aaa - setup' do
role = Role.lookup(name: 'Admin')
roles = Role.where(name: %w(Admin Agent))
group = Group.lookup(name: 'Users')
admin_user = User.create_or_update(
login: 'admin',
@@ -15,392 +14,200 @@ class ActivityStreamTest < ActiveSupport::TestCase
email: 'bob@example.com',
password: 'some_pass',
active: true,
role_ids: [role.id],
roles: roles,
group_ids: [group.id],
updated_by_id: 1,
created_by_id: 1
)
current_user = User.lookup(email: 'nicole.braun@zammad.org')
activity_record_delay = if ENV['ZAMMAD_ACTIVITY_RECORD_DELAY']
ENV['ZAMMAD_ACTIVITY_RECORD_DELAY'].to_i.seconds
else
90.seconds
end
end
test 'ticket+user' do
tests = [
ticket = Ticket.create(
group_id: Group.lookup(name: 'Users').id,
customer_id: current_user.id,
owner_id: User.lookup(login: '-').id,
title: 'Unit Test 1 (äöüß)!',
state_id: Ticket::State.lookup(name: 'new').id,
priority_id: Ticket::Priority.lookup(name: '2 normal').id,
updated_by_id: current_user.id,
created_by_id: current_user.id,
)
travel 2.seconds
# test 1
{
create: {
ticket: {
group_id: Group.lookup(name: 'Users').id,
customer_id: current_user.id,
owner_id: User.lookup(login: '-').id,
title: 'Unit Test 1 (äöüß)!',
state_id: Ticket::State.lookup(name: 'new').id,
priority_id: Ticket::Priority.lookup(name: '2 normal').id,
updated_by_id: current_user.id,
created_by_id: current_user.id,
},
article: {
updated_by_id: current_user.id,
created_by_id: current_user.id,
type_id: Ticket::Article::Type.lookup(name: 'phone').id,
sender_id: Ticket::Article::Sender.lookup(name: 'Customer').id,
from: 'Unit Test <unittest@example.com>',
body: 'Unit Test 123',
internal: false,
},
},
update: {
ticket: {
title: 'Unit Test 1 (äöüß) - update!',
state_id: Ticket::State.lookup(name: 'open').id,
priority_id: Ticket::Priority.lookup(name: '1 low').id,
},
},
update2: {
ticket: {
title: 'Unit Test 2 (äöüß) - update!',
priority_id: Ticket::Priority.lookup(name: '2 normal').id,
},
},
check: [
{
result: true,
object: 'Ticket',
type: 'update',
},
{
result: true,
object: 'Ticket::Article',
type: 'create',
},
{
result: true,
object: 'Ticket',
type: 'create',
},
{
result: false,
object: 'User',
type: 'update',
o_id: current_user.id,
},
]
},
]
tickets = []
tests.each { |test|
article = Ticket::Article.create(
ticket_id: ticket.id,
updated_by_id: current_user.id,
created_by_id: current_user.id,
type_id: Ticket::Article::Type.lookup(name: 'phone').id,
sender_id: Ticket::Article::Sender.lookup(name: 'Customer').id,
from: 'Unit Test <unittest@example.com>',
body: 'Unit Test 123',
internal: false,
)
ticket = Ticket.create(test[:create][:ticket])
test[:check][0][:o_id] = ticket.id
test[:check][2][:o_id] = ticket.id
test[:check][2][:created_at] = ticket.created_at
test[:check][2][:created_by_id] = current_user.id
sleep 2
travel 100.seconds
ticket.update_attributes(
title: 'Unit Test 1 (äöüß) - update!',
state_id: Ticket::State.lookup(name: 'open').id,
priority_id: Ticket::Priority.lookup(name: '1 low').id,
)
updated_at = ticket.updated_at
test[:create][:article][:ticket_id] = ticket.id
article = Ticket::Article.create(test[:create][:article])
test[:check][1][:o_id] = article.id
test[:check][1][:created_at] = article.created_at
test[:check][1][:created_by_id] = current_user.id
travel 1.second
ticket.update_attributes(
title: 'Unit Test 2 (äöüß) - update!',
priority_id: Ticket::Priority.lookup(name: '2 normal').id,
)
assert_equal(ticket.class.to_s, 'Ticket')
assert_equal(article.class.to_s, 'Ticket::Article')
# check activity_stream
stream = admin_user.activity_stream(4)
assert_equal(stream[0]['group_id'], ticket.group_id)
assert_equal(stream[0]['o_id'], ticket.id)
assert_equal(stream[0]['created_by_id'], current_user.id)
assert_equal(stream[0]['created_at'].to_s, updated_at.to_s)
assert_equal(stream[0]['object'], 'Ticket')
assert_equal(stream[0]['type'], 'update')
assert_equal(stream[1]['group_id'], ticket.group_id)
assert_equal(stream[1]['o_id'], article.id)
assert_equal(stream[1]['created_by_id'], current_user.id)
assert_equal(stream[1]['created_at'].to_s, article.created_at.to_s)
assert_equal(stream[1]['object'], 'Ticket::Article')
assert_equal(stream[1]['type'], 'create')
assert_equal(stream[2]['group_id'], ticket.group_id)
assert_equal(stream[2]['o_id'], ticket.id)
assert_equal(stream[2]['created_by_id'], current_user.id)
assert_equal(stream[2]['created_at'].to_s, ticket.created_at.to_s)
assert_equal(stream[2]['object'], 'Ticket')
assert_equal(stream[2]['type'], 'create')
assert_not(stream[3])
# update ticket
if test[:update][:ticket]
ticket.update_attributes(test[:update][:ticket])
stream = current_user.activity_stream(4)
assert(stream.empty?)
# check updated user
test[:check][3][:o_id] = current_user.id
test[:check][3][:created_at] = ticket.created_at
test[:check][3][:created_by_id] = current_user.id
end
if test[:update2][:ticket]
ticket = Ticket.find(ticket.id)
ticket.update_attributes(test[:update2][:ticket])
end
if test[:update][:article]
article.update_attributes(test[:update][:article])
end
sleep activity_record_delay + 1
if test[:update][:ticket]
ticket.update_attributes(test[:update][:ticket])
end
if test[:update2][:ticket]
ticket.update_attributes(test[:update2][:ticket])
end
# remember ticket
tickets.push ticket
# check activity_stream
activity_stream_check(admin_user.activity_stream(3), test[:check])
}
# delete tickets
tickets.each { |ticket|
ticket_id = ticket.id
ticket.destroy
found = Ticket.where(id: ticket_id).first
assert_not(found, 'Ticket destroyed')
}
# cleanup
ticket.destroy
travel_back
end
test 'organization' do
tests = [
# test 1
{
create: {
organization: {
name: 'some name',
updated_by_id: current_user.id,
created_by_id: current_user.id,
},
},
update1: {
organization: {
name: 'some name (äöüß)',
},
},
update2: {
organization: {
name: 'some name 2 (äöüß)',
},
},
check: [
{
result: true,
object: 'Organization',
type: 'update',
},
{
result: true,
object: 'Organization',
type: 'create',
},
]
},
]
organizations = []
tests.each { |test|
organization = Organization.create(
name: 'some name',
updated_by_id: current_user.id,
created_by_id: current_user.id,
)
travel 100.seconds
assert_equal(organization.class, Organization)
organization = Organization.create(test[:create][:organization])
test[:check][0][:o_id] = organization.id
test[:check][0][:created_at] = organization.created_at
test[:check][0][:created_by_id] = current_user.id
sleep 2
organization.update_attributes(name: 'some name (äöüß)')
updated_at = organization.updated_at
assert_equal(organization.class.to_s, 'Organization')
travel 10.seconds
organization.update_attributes(name: 'some name 2 (äöüß)')
if test[:update1][:organization]
organization.update_attributes(test[:update1][:organization])
test[:check][1][:o_id] = organization.id
test[:check][1][:updated_at] = organization.updated_at
test[:check][1][:created_by_id] = current_user.id
sleep activity_record_delay - 1
end
# check activity_stream
stream = admin_user.activity_stream(3)
assert_not(stream[0]['group_id'])
assert_equal(stream[0]['o_id'], organization.id)
assert_equal(stream[0]['created_by_id'], current_user.id)
assert_equal(stream[0]['created_at'].to_s, updated_at.to_s)
assert_equal(stream[0]['object'], 'Organization')
assert_equal(stream[0]['type'], 'update')
assert_not(stream[1]['group_id'])
assert_equal(stream[1]['o_id'], organization.id)
assert_equal(stream[1]['created_by_id'], current_user.id)
assert_equal(stream[1]['created_at'].to_s, organization.created_at.to_s)
assert_equal(stream[1]['object'], 'Organization')
assert_equal(stream[1]['type'], 'create')
assert_not(stream[2])
if test[:update2][:organization]
organization.update_attributes(test[:update2][:organization])
end
stream = current_user.activity_stream(4)
assert(stream.empty?)
# remember organization
organizations.push organization
# check activity_stream
activity_stream_check(admin_user.activity_stream(2), test[:check])
}
# delete tickets
organizations.each { |organization|
organization_id = organization.id
organization.destroy
found = Organization.where(id: organization_id).first
assert( !found, 'Organization destroyed')
}
# cleanup
organization.destroy
travel_back
end
test 'user with update check false' do
tests = [
user = User.create(
login: 'someemail@example.com',
email: 'someemail@example.com',
firstname: 'Bob Smith II',
updated_by_id: current_user.id,
created_by_id: current_user.id,
)
assert_equal(user.class, User)
user.update_attributes(
firstname: 'Bob U',
lastname: 'Smith U',
)
# test 1
{
create: {
user: {
login: 'someemail@example.com',
email: 'someemail@example.com',
firstname: 'Bob Smith II',
updated_by_id: current_user.id,
created_by_id: current_user.id,
},
},
update1: {
user: {
firstname: 'Bob U',
lastname: 'Smith U',
},
},
check: [
{
result: true,
object: 'User',
type: 'create',
},
{
result: false,
object: 'User',
type: 'update',
},
]
},
]
users = []
tests.each { |test|
# check activity_stream
stream = admin_user.activity_stream(3)
assert_not(stream[0]['group_id'])
assert_equal(stream[0]['o_id'], user.id)
assert_equal(stream[0]['created_by_id'], current_user.id)
assert_equal(stream[0]['created_at'].to_s, user.created_at.to_s)
assert_equal(stream[0]['object'], 'User')
assert_equal(stream[0]['type'], 'create')
assert_not(stream[1])
user = User.create(test[:create][:user])
test[:check][0][:o_id] = user.id
test[:check][0][:created_at] = user.created_at
test[:check][0][:created_by_id] = current_user.id
stream = current_user.activity_stream(4)
assert(stream.empty?)
assert_equal(user.class.to_s, 'User')
if test[:update1][:user]
user.update_attributes(test[:update1][:user])
test[:check][1][:o_id] = user.id
test[:check][1][:updated_at] = user.updated_at
test[:check][1][:created_by_id] = current_user.id
end
# remember organization
users.push user
# check activity_stream
activity_stream_check(admin_user.activity_stream(3), test[:check])
}
# delete tickets
users.each { |user|
user_id = user.id
user.destroy
found = User.where( id: user_id ).first
assert_not(found, 'User destroyed')
}
# cleanup
user.destroy
travel_back
end
test 'user with update check true' do
tests = [
# test 1
{
create: {
user: {
login: 'someemail@example.com',
email: 'someemail@example.com',
firstname: 'Bob Smith II',
updated_by_id: current_user.id,
created_by_id: current_user.id,
},
},
update1: {
user: {
firstname: 'Bob U',
lastname: 'Smith U',
},
},
update2: {
user: {
firstname: 'Bob',
lastname: 'Smith',
},
},
check: [
{
result: true,
object: 'User',
type: 'update',
},
{
result: true,
object: 'User',
type: 'create',
},
]
},
]
users = []
tests.each { |test|
user = User.create(
login: 'someemail@example.com',
email: 'someemail@example.com',
firstname: 'Bob Smith II',
updated_by_id: current_user.id,
created_by_id: current_user.id,
)
travel 100.seconds
assert_equal(user.class, User)
user = User.create(test[:create][:user])
test[:check][0][:o_id] = user.id
test[:check][0][:created_at] = user.created_at
test[:check][0][:created_by_id] = current_user.id
user.update_attributes(
firstname: 'Bob U',
lastname: 'Smith U',
)
updated_at = user.updated_at
assert_equal(user.class.to_s, 'User')
travel 10.seconds
user.update_attributes(
firstname: 'Bob',
lastname: 'Smith',
)
if test[:update1][:user]
user.update_attributes(test[:update1][:user])
test[:check][1][:o_id] = user.id
test[:check][1][:updated_at] = user.updated_at
test[:check][1][:created_by_id] = current_user.id
end
# check activity_stream
stream = admin_user.activity_stream(3)
assert_not(stream[0]['group_id'])
assert_equal(stream[0]['o_id'], user.id)
assert_equal(stream[0]['created_by_id'], current_user.id)
assert_equal(stream[0]['created_at'].to_s, updated_at.to_s)
assert_equal(stream[0]['object'], 'User')
assert_equal(stream[0]['type'], 'update')
assert_not(stream[1]['group_id'])
assert_equal(stream[1]['o_id'], user.id)
assert_equal(stream[1]['created_by_id'], current_user.id)
assert_equal(stream[1]['created_at'].to_s, user.created_at.to_s)
assert_equal(stream[1]['object'], 'User')
assert_equal(stream[1]['type'], 'create')
assert_not(stream[2])
# to verify update which need to be logged
sleep activity_record_delay + 1
stream = current_user.activity_stream(4)
assert(stream.empty?)
if test[:update2][:user]
user.update_attributes(test[:update2][:user])
end
# remember organization
users.push user
# check activity_stream
activity_stream_check(admin_user.activity_stream(2), test[:check])
}
# delete tickets
users.each { |user|
user_id = user.id
user.destroy
found = User.where(id: user_id).first
assert(!found, 'User destroyed')
}
end
def activity_stream_check(activity_stream_list, checks)
#activity_stream_list = activity_stream_list.reverse
#puts 'AS ' + activity_stream_list.inspect
check_count = 0
checks.each { |check_item|
check_count += 1
#puts '+++++++++++'
#puts check_item.inspect
check_list = 0
activity_stream_list.each { |item|
check_list += 1
next if check_list != check_count
#next if match
#puts '--------'
#puts item.inspect
#puts check_item.inspect
if check_item[:result]
assert_equal(check_item[:object], item['object'])
assert_equal(check_item[:type], item['type'])
assert_equal(check_item[:o_id], item['o_id'])
elsif check_item[:object] == item['object'] && check_item[:type] == item['type'] && check_item[:o_id] == item['o_id']
assert(false, "entry should not exist #{item['object']}/#{item['type']}/#{item['o_id']}")
end
}
}
# cleanup
user.destroy
travel_back
end
end

View File

@@ -6,7 +6,7 @@ class AssetsTest < ActiveSupport::TestCase
roles = Role.where(name: %w(Agent Admin))
groups = Group.all
org = Organization.create_or_update(
org1 = Organization.create_or_update(
name: 'some user org',
updated_by_id: 1,
created_by_id: 1,
@@ -21,7 +21,7 @@ class AssetsTest < ActiveSupport::TestCase
active: true,
updated_by_id: 1,
created_by_id: 1,
organization_id: org.id,
organization_id: org1.id,
roles: roles,
groups: groups,
)
@@ -54,10 +54,10 @@ class AssetsTest < ActiveSupport::TestCase
user3 = User.find(user3.id)
assets = user3.assets({})
org = Organization.find(org.id)
attributes = org.attributes_with_associations
org1 = Organization.find(org1.id)
attributes = org1.attributes_with_associations
attributes.delete('user_ids')
assert( diff(attributes, assets[:Organization][org.id]), 'check assets')
assert( diff(attributes, assets[:Organization][org1.id]), 'check assets')
user1 = User.find(user1.id)
attributes = user1.attributes_with_associations
@@ -84,8 +84,8 @@ class AssetsTest < ActiveSupport::TestCase
assert( diff(attributes, assets[:User][user3.id]), 'check assets' )
# touch org, check if user1 has changed
sleep 2
org2 = Organization.find(org.id)
travel 2.seconds
org2 = Organization.find(org1.id)
org2.note = "some note...#{rand(9_999_999_999_999)}"
org2.save
@@ -105,7 +105,7 @@ class AssetsTest < ActiveSupport::TestCase
assets = user3.assets({})
attributes = org2.attributes_with_associations
attributes.delete('user_ids')
assert( diff(attributes, assets[:Organization][org.id]), 'check assets')
assert( diff(attributes, assets[:Organization][org1.id]), 'check assets')
user1 = User.find(user1.id)
attributes = user1.attributes_with_associations
@@ -130,6 +130,13 @@ class AssetsTest < ActiveSupport::TestCase
attributes.delete('token_ids')
attributes.delete('authorization_ids')
assert( diff(attributes, assets[:User][user3.id]), 'check assets' )
travel_back
user1.destroy
user2.destroy
user3.destroy
org1.destroy
org2.destroy
end
@@ -148,8 +155,8 @@ class AssetsTest < ActiveSupport::TestCase
roles: roles,
)
roles = Role.where( name: %w(Customer) )
org = Organization.create_or_update(
roles = Role.where( name: %w(Customer) )
org = Organization.create_or_update(
name: 'some customer org',
updated_by_id: admin1.id,
created_by_id: 1,
@@ -232,7 +239,7 @@ class AssetsTest < ActiveSupport::TestCase
assert_nil( assets[:User][user3.id], 'check assets' )
# touch user 2, check if org has changed
sleep 2
travel 2.seconds
user_new_2 = User.find(user2.id)
user_new_2.lastname = 'assets2'
user_new_2.save
@@ -261,6 +268,13 @@ class AssetsTest < ActiveSupport::TestCase
attributes.delete('token_ids')
attributes.delete('authorization_ids')
assert( diff(attributes, assets[:User][user_new_2.id]), 'check assets' )
travel_back
user1.destroy
user2.destroy
user3.destroy
org.destroy
org_new.destroy
end

View File

@@ -19,7 +19,7 @@ class CalendarTest < ActiveSupport::TestCase
updated_by_id: 1,
created_by_id: 1,
)
sleep 1
travel 1.second
calendar2 = Calendar.create_or_update(
name: 'US 2',
timezone: 'America/Los_Angeles',
@@ -88,6 +88,7 @@ class CalendarTest < ActiveSupport::TestCase
assert_equal(true, calendar2.default)
assert_equal(false, calendar3.default)
travel_back
end
end

View File

@@ -208,19 +208,19 @@ class ChatTest < ActiveSupport::TestCase
content: 'message 1',
created_by_id: agent1.id,
)
sleep 1
travel 1.second
Chat::Message.create(
chat_session_id: chat_session6.id,
content: 'message 2',
created_by_id: agent1.id,
)
sleep 1
travel 1.second
Chat::Message.create(
chat_session_id: chat_session6.id,
content: 'message 3',
created_by_id: agent1.id,
)
sleep 1
travel 1.second
Chat::Message.create(
chat_session_id: chat_session6.id,
content: 'message 4',
@@ -351,7 +351,7 @@ class ChatTest < ActiveSupport::TestCase
assert_equal(1, agent_state[:seads_available])
assert_equal(4, agent_state[:seads_total])
assert_equal(false, agent_state[:active])
travel_back
end
end

View File

@@ -211,4 +211,20 @@ text
assert_equal(html_should, html_with_fixes)
end
test 'from checks' do
quoted_in_one_line = Channel::EmailBuild.recipient_line('Somebody @ "Company"', 'some.body@example.com')
assert_equal('"Somebody @ \"Company\"" <some.body@example.com>', quoted_in_one_line)
quoted_in_one_line = Channel::EmailBuild.recipient_line('Somebody', 'some.body@example.com')
assert_equal('Somebody <some.body@example.com>', quoted_in_one_line)
quoted_in_one_line = Channel::EmailBuild.recipient_line('Somebody | Some Org', 'some.body@example.com')
assert_equal('"Somebody | Some Org" <some.body@example.com>', quoted_in_one_line)
quoted_in_one_line = Channel::EmailBuild.recipient_line('Test Master Agent via Support', 'some.body@example.com')
assert_equal('"Test Master Agent via Support" <some.body@example.com>', quoted_in_one_line)
end
end

View File

@@ -27,11 +27,13 @@ class EmailProcessBounceTest < ActiveSupport::TestCase
updated_by_id: 1,
created_by_id: 1,
)
sleep 1
travel 1.second
email_raw_string = IO.binread('test/fixtures/mail33-undelivered-mail-returned-to-sender.box')
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('new', ticket_p.state.name)
travel_back
ticket.destroy
end
end

View File

@@ -84,47 +84,92 @@ no reference "
setting_orig = Setting.get('postmaster_follow_up_search_in')
Setting.set('postmaster_follow_up_search_in', %w(body attachment references))
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
assert_equal(ticket.id, ticket_p.id)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
assert_equal(ticket.id, ticket_p.id)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
assert_equal(ticket.id, ticket_p.id)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
assert_equal(ticket.id, ticket_p.id)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
assert_equal(ticket.id, ticket_p.id)
Setting.set('postmaster_follow_up_search_in', nil)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
assert_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
assert_not_equal(ticket.id, ticket_p.id)
Setting.set('postmaster_follow_up_search_in', 'references')
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
assert_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
assert_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
assert_equal(ticket.id, ticket_p.id)
Setting.set('postmaster_follow_up_search_in', setting_orig)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
assert_equal(ticket.id, ticket_p.id)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
assert_not_equal(ticket.id, ticket_p.id)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
assert_not_equal(ticket.id, ticket_p.id)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
assert_not_equal(ticket.id, ticket_p.id)
sleep 1
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
assert_not_equal(ticket.id, ticket_p.id)
travel_back
end
test 'process with follow up check with two external reference headers' do
@@ -141,7 +186,7 @@ test 123
"
ticket_p1, article_p1, user_p1 = Channel::EmailParser.new.process({}, data1)
sleep 1
travel 1.second
data1 = "From: me@example.com
To: z@example.com
@@ -166,7 +211,7 @@ test 123
assert_equal(ticket_p2.id, ticket_p3.id)
Setting.set('postmaster_follow_up_search_in', setting_orig)
travel_back
end
test 'process with follow up check - with auto responses and no T# in subject_build' do
@@ -193,7 +238,7 @@ test 123
updated_by_id: 1,
created_by_id: 1,
)
sleep 1
travel 1.second
# auto response without T# in subject, find follow up by references header
email_raw_string = "From: bob@example.com
@@ -211,6 +256,7 @@ Some Text"
ticket = Ticket.find(ticket.id)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket.state.name)
travel_back
end
test 'process with follow up check - email with more forgein T#\'s in subject' do
@@ -237,7 +283,7 @@ Some Text"
updated_by_id: 1,
created_by_id: 1,
)
sleep 1
travel 1.second
system_id = Setting.get('system_id')
ticket_hook = Setting.get('ticket_hook')
@@ -255,6 +301,7 @@ Some Text"
ticket = Ticket.find(ticket.id)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket.state.name)
travel_back
end
test 'process with follow up check - ticket initiated by customer without T# in subject and other people in Cc reply to all' do
@@ -322,4 +369,41 @@ Some Text"
Setting.set('postmaster_follow_up_search_in', setting_orig)
end
test 'process with follow up check - with none ticket# in subject' do
setting_orig = Setting.get('postmaster_follow_up_search_in')
Setting.set('postmaster_follow_up_search_in', [])
ticket_hook_position_orig = Setting.get('ticket_hook_position')
Setting.set('ticket_hook_position', 'none')
subject = 'some title'
email_raw_string = "From: me@example.com
To: bob@example.com
Subject: #{subject}
Message-ID: <123456789-follow-up-test-ticket_hook_position-none@linuxhotel.de>
Some Text"
ticket_p1, article_1, user_1, mail = Channel::EmailParser.new.process({}, email_raw_string)
ticket1 = Ticket.find(ticket_p1.id)
assert_equal(subject, ticket1.title)
# follow up possible because same subject
subject = 'new subject lalala'
email_raw_string = "From: bob@example.com
To: me@example.com
Subject: AW: #{subject}
Message-ID: <123456789-follow-up-test-ticket_hook_position-none-2@linuxhotel.de>
References: <123456789-follow-up-test-ticket_hook_position-none@linuxhotel.de>
Some Text"
ticket_p2, article_p2, user_p2, mail = Channel::EmailParser.new.process({}, email_raw_string)
ticket2 = Ticket.find(ticket_p2.id)
assert_equal(ticket1.id, ticket2.id)
Setting.set('ticket_hook_position', ticket_hook_position_orig)
Setting.set('postmaster_follow_up_search_in', setting_orig)
end
end

View File

@@ -27,7 +27,7 @@ class EmailProcessOutOfOfficeTest < ActiveSupport::TestCase
updated_by_id: 1,
created_by_id: 1,
)
sleep 1
travel 1.second
# exchange out of office example #1
email_raw_string = "From: me@example.com
@@ -86,7 +86,7 @@ Some Text"
ticket = Ticket.find(ticket.id)
assert_equal(ticket.id, ticket_p.id)
assert_equal('closed', ticket.state.name)
travel_back
end
test 'process with out of office check - zimbra' do
@@ -113,7 +113,7 @@ Some Text"
updated_by_id: 1,
created_by_id: 1,
)
sleep 1
travel 1.second
# exchange out of office example #1
email_raw_string = "From: me@example.com
@@ -164,7 +164,7 @@ Some Text"
ticket = Ticket.find(ticket.id)
assert_equal(ticket.id, ticket_p.id)
assert_equal('closed', ticket.state.name)
travel_back
end
test 'process with out of office check - cloud' do
@@ -191,7 +191,7 @@ Some Text"
updated_by_id: 1,
created_by_id: 1,
)
sleep 1
travel 1.second
# exchange out of office example #1
email_raw_string = "From: me@example.com
@@ -237,7 +237,7 @@ Some Text"
ticket = Ticket.find(ticket.id)
assert_equal(ticket.id, ticket_p.id)
assert_equal('closed', ticket.state.name)
travel_back
end
test 'process with out of office check - gmail' do
@@ -264,7 +264,7 @@ Some Text"
updated_by_id: 1,
created_by_id: 1,
)
sleep 1
travel 1.second
# gmail out of office example #1
email_raw_string = "From: me@example.com
@@ -294,6 +294,7 @@ Some Text 2"
ticket = Ticket.find(ticket.id)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
travel_back
end
end

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,74 @@
# encoding: utf-8
require 'test_helper'
class HtmlSanitizerTest < ActiveSupport::TestCase
test 'xss' do
assert_equal(HtmlSanitizer.strict('<b>123</b>'), '<b>123</b>')
assert_equal(HtmlSanitizer.strict('<script><b>123</b></script>'), '&lt;b&gt;123&lt;/b&gt;')
assert_equal(HtmlSanitizer.strict('<script><style><b>123</b></style></script>'), '&lt;style&gt;&lt;b&gt;123&lt;/b&gt;&lt;/style&gt;')
assert_equal(HtmlSanitizer.strict('<abc><i><b>123</b><bbb>123</bbb></i></abc>'), '<i><b>123</b>123</i>')
assert_equal(HtmlSanitizer.strict('<abc><i><b>123</b><bbb>123<i><ccc>abc</ccc></i></bbb></i></abc>'), '<i><b>123</b>123<i>abc</i></i>')
assert_equal(HtmlSanitizer.strict('<not_existing>123</not_existing>'), '123')
assert_equal(HtmlSanitizer.strict('<script type="text/javascript">alert("XSS!");</script>'), 'alert("XSS!");')
assert_equal(HtmlSanitizer.strict('<SCRIPT SRC=http://xss.rocks/xss.js></SCRIPT>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="javascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC=javascript:alert(\'XSS\')>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC=JaVaScRiPt:alert(\'XSS\')>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC=`javascript:alert("RSnake says, \'XSS\'")`>'), '')
assert_equal(HtmlSanitizer.strict('<IMG """><SCRIPT>alert("XSS")</SCRIPT>">'), '<img>alert("XSS")"&gt;')
assert_equal(HtmlSanitizer.strict('<IMG SRC=# onmouseover="alert(\'xxs\')">'), '<img src="#">')
assert_equal(HtmlSanitizer.strict('<IMG SRC="jav ascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="jav&#x09;ascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="jav&#x0A;ascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="jav&#x0D;ascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC=" &#14; javascript:alert(\'XSS\');">'), '<img src="">')
assert_equal(HtmlSanitizer.strict('<SCRIPT/XSS SRC="http://xss.rocks/xss.js"></SCRIPT>'), '')
assert_equal(HtmlSanitizer.strict('<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>'), '')
assert_equal(HtmlSanitizer.strict('<SCRIPT/SRC="http://xss.rocks/xss.js"></SCRIPT>'), '')
assert_equal(HtmlSanitizer.strict('<<SCRIPT>alert("XSS");//<</SCRIPT>'), '&lt;alert("XSS");//&lt;')
assert_equal(HtmlSanitizer.strict('<SCRIPT SRC=http://xss.rocks/xss.js?< B >'), '')
assert_equal(HtmlSanitizer.strict('<SCRIPT SRC=//xss.rocks/.j>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="javascript:alert(\'XSS\')"'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="javascript:alert(\'XSS\')" abc<b>123</b>'), '123')
assert_equal(HtmlSanitizer.strict('<iframe src=http://xss.rocks/scriptlet.html <'), '')
assert_equal(HtmlSanitizer.strict('</script><script>alert(\'XSS\');</script>'), 'alert(\'XSS\');')
assert_equal(HtmlSanitizer.strict('<STYLE>li {list-style-image: url("javascript:alert(\'XSS\')");}</STYLE><UL><LI>XSS</br>'), '<ul><li>XSS</li></ul>')
assert_equal(HtmlSanitizer.strict('<IMG SRC=\'vbscript:msgbox("XSS")\'>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="livescript:[code]">'), '')
assert_equal(HtmlSanitizer.strict('<svg/onload=alert(\'XSS\')>'), '')
assert_equal(HtmlSanitizer.strict('<BODY ONLOAD=alert(\'XSS\')>'), '')
assert_equal(HtmlSanitizer.strict('<LINK REL="stylesheet" HREF="javascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<STYLE>@import\'http://xss.rocks/xss.css\';</STYLE>'), '')
assert_equal(HtmlSanitizer.strict('<META HTTP-EQUIV="Link" Content="<http://xss.rocks/xss.css>; REL=stylesheet">'), '')
assert_equal(HtmlSanitizer.strict('<IMG STYLE="java/*XSS*/script:(alert(\'XSS\'), \'\')">'), '<img>')
assert_equal(HtmlSanitizer.strict('<IMG src="java/*XSS*/script:(alert(\'XSS\'), \'\')">'), '')
assert_equal(HtmlSanitizer.strict('<IFRAME SRC="javascript:alert(\'XSS\');"></IFRAME>'), '')
assert_equal(HtmlSanitizer.strict('<TABLE><TD BACKGROUND="javascript:alert(\'XSS\')">'), '<table><td></td></table>')
assert_equal(HtmlSanitizer.strict('<DIV STYLE="background-image: url(javascript:alert(\'XSS\'), \'\')">'), '<div></div>')
assert_equal(HtmlSanitizer.strict('<a href="/some/path">test</a>'), '<a href="/some/path">test</a>')
assert_equal(HtmlSanitizer.strict('<a href="https://some/path">test</a>'), '<a href="https://some/path" rel="nofollow" target="_blank">test</a>')
assert_equal(HtmlSanitizer.strict('<XML ID="xss"><I><B><IMG SRC="javas<!-- -->cript:alert(\'XSS\')"></B></I></XML>'), '<i><b></b></i>')
assert_equal(HtmlSanitizer.strict('<IMG SRC="javas<!-- -->cript:alert(\'XSS\')">'), '')
assert_equal(HtmlSanitizer.strict(' <HEAD><META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=UTF-7"> </HEAD>+ADw-SCRIPT+AD4-alert(\'XSS\');+ADw-/SCRIPT+AD4-'), ' +ADw-SCRIPT+AD4-alert(\'XSS\');+ADw-/SCRIPT+AD4-')
assert_equal(HtmlSanitizer.strict('<SCRIPT a=">" SRC="httx://xss.rocks/xss.js"></SCRIPT>'), '')
assert_equal(HtmlSanitizer.strict('<A HREF="h
tt p://6 6.000146.0x7.147/">XSS</A>'), '<a href="h%0Att%20%20p://6%206.000146.0x7.147/" rel="nofollow" target="_blank">XSS</a>')
assert_equal(HtmlSanitizer.strict('<A HREF="//www.google.com/">XSS</A>'), '<a href="//www.google.com/" rel="nofollow" target="_blank">XSS</a>')
assert_equal(HtmlSanitizer.strict('<form id="test"></form><button form="test" formaction="javascript:alert(1)">X</button>'), 'X')
assert_equal(HtmlSanitizer.strict('<maction actiontype="statusline#http://google.com" xlink:href="javascript:alert(2)">CLICKME</maction>'), 'CLICKME')
assert_equal(HtmlSanitizer.strict('<a xlink:href="javascript:alert(2)">CLICKME</a>'), '<a>CLICKME</a>')
assert_equal(HtmlSanitizer.strict('<!--<img src="--><img src=x onerror=alert(1)//">'), '<img src="x">')
assert_equal(HtmlSanitizer.strict('<![><img src="]><img src=x onerror=alert(1)//">'), '<img src="%5D&gt;&lt;img%20src=x%20onerror=alert(1)//">')
assert_equal(HtmlSanitizer.strict('<svg><![CDATA[><image xlink:href="]]><img src=xx:x onerror=alert(2)//"></svg>'), '')
assert_equal(HtmlSanitizer.strict('<abc><img src="</abc><img src=x onerror=alert(1)//">'), '<img src="&lt;/abc&gt;&lt;img%20src=x%20onerror=alert(1)//">')
assert_equal(HtmlSanitizer.strict('<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>'), '')
assert_equal(HtmlSanitizer.strict('<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></embed>'), '')
assert_equal(HtmlSanitizer.strict('<img[a][b]src=x[d]onerror[c]=[e]"alert(1)">'), '<img>')
assert_equal(HtmlSanitizer.strict('<a href="[a]java[b]script[c]:alert(1)">XXX</a>'), '<a>XXX</a>')
assert_equal(HtmlSanitizer.strict('<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>'), 'alert(1)')
assert_equal(HtmlSanitizer.strict('<a style="position:fixed;top:0;left:0;width: 260px;height:100vh;background-color:red;display: block;" href="http://example.com"></a>'), '<a style="width: 260px;height:100vh;" href="http://example.com" rel="nofollow" target="_blank"></a>')
end
end

View File

@@ -407,6 +407,26 @@ class ObjectManagerTest < ActiveSupport::TestCase
}
assert_equal(false, ObjectManager::Attribute.pending_migration?)
assert_raises(RuntimeError) {
attribute18 = ObjectManager::Attribute.add(
object: 'Ticket',
name: 'delete',
display: 'Test 18',
data_type: 'input',
data_option: {
maxlength: 200,
type: 'text',
null: false,
},
active: true,
screens: {},
position: 20,
created_by_id: 1,
updated_by_id: 1,
)
}
assert_equal(false, ObjectManager::Attribute.pending_migration?)
end
test 'b object manager attribute' do

View File

@@ -81,7 +81,7 @@ class OrganizationRefObjectTouchTest < ActiveSupport::TestCase
assert_equal(ticket.customer.id, customer1.id)
assert_equal(ticket.organization.id, organization1.id)
sleep 4
travel 4.seconds
organization1.name = 'Ref Object Update Org 1/1'
organization1.save
@@ -101,7 +101,7 @@ class OrganizationRefObjectTouchTest < ActiveSupport::TestCase
assert(false, 'customer1.updated_at has not been updated')
end
sleep 4
travel 4.seconds
customer2.organization_id = organization1.id
customer2.save
@@ -123,5 +123,6 @@ class OrganizationRefObjectTouchTest < ActiveSupport::TestCase
delete = ticket.destroy
assert(delete, 'ticket destroy')
travel_back
end
end

View File

@@ -123,4 +123,48 @@ class PermissionTest < ActiveSupport::TestCase
end
test 'user permission with childs' do
permission1 = Permission.create_or_update(
name: 'admin.permission_child1',
note: 'Admin Interface',
preferences: {},
active: true,
)
permission2 = Permission.create_or_update(
name: 'admin.permission_child2',
note: 'Admin Interface',
preferences: {},
active: false,
)
role_permission1 = Role.create_or_update(
name: 'AdminPermissionChild1',
note: 'To configure your permission child1.',
preferences: {
not: ['Customer'],
},
default_at_signup: false,
updated_by_id: 1,
created_by_id: 1,
)
role_permission1.permission_grand('admin')
user_with_permission1 = User.create_or_update(
login: 'setting-permission-child1',
firstname: 'Setting',
lastname: 'Admin Permission Child1',
email: 'setting-admin-permission-child1@example.com',
password: 'some_pw',
active: true,
roles: [role_permission1],
updated_by_id: 1,
created_by_id: 1,
)
assert(user_with_permission1.permissions_with_child_ids.include?(permission1.id))
assert_not(user_with_permission1.permissions_with_child_ids.include?(permission2.id))
assert(user_with_permission1.permissions_with_child_ids.include?(Permission.find_by(name: 'admin').id))
# cleanup
user_with_permission1.destroy
role_permission1.destroy
end
end

View File

@@ -29,11 +29,11 @@ class RecentViewTest < ActiveSupport::TestCase
RecentView.user_log_destroy(user1)
RecentView.log(ticket1.class.to_s, ticket1.id, user1)
sleep 1
travel 1.second
RecentView.log(ticket2.class.to_s, ticket2.id, user1)
sleep 1
travel 1.second
RecentView.log(ticket1.class.to_s, ticket1.id, user1)
sleep 1
travel 1.second
RecentView.log(ticket1.class.to_s, ticket1.id, user1)
list = RecentView.list(user1)
@@ -54,6 +54,7 @@ class RecentViewTest < ActiveSupport::TestCase
list = RecentView.list(user1)
assert(!list[0], 'check if recent view list is empty')
travel_back
end
test 'existing tests' do

View File

@@ -19,7 +19,7 @@ class SessionBasicTest < ActiveSupport::TestCase
assert_equal(false, result, 'check 1 - expired')
# should be expired
sleep 2
travel 2.seconds
result = Sessions::CacheIn.expired('last_run_test')
assert_equal(true, result, 'check 1 - expired')
@@ -32,7 +32,7 @@ class SessionBasicTest < ActiveSupport::TestCase
assert_equal(false, result, 'check 1 - expired')
# ignore expired
sleep 2
travel 2.seconds
result = Sessions::CacheIn.get('last_run_test', ignore_expire: true)
assert_equal(true, result, 'check 1 - ignore_expire')
@@ -50,6 +50,7 @@ class SessionBasicTest < ActiveSupport::TestCase
Sessions::CacheIn.delete('last_run_delete')
result = Sessions::CacheIn.get('last_run_delete')
assert_equal(nil, nil, 'check delete')
travel_back
end
test 'c session create / update' do
@@ -124,14 +125,14 @@ class SessionBasicTest < ActiveSupport::TestCase
# next check should be empty
result1 = collection_client1.push
assert(!result1, 'check collections - recall')
sleep 1
travel 1.second
result2 = collection_client2.push
assert(!result2, 'check collections - recall')
# change collection
group = Group.first
group.touch
sleep 4
travel 4.seconds
# get whole collections
result1 = collection_client1.push
@@ -149,7 +150,7 @@ class SessionBasicTest < ActiveSupport::TestCase
# change collection
group = Group.create(name: "SomeGroup::#{rand(999_999)}", active: true)
sleep 4
travel 4.seconds
# get whole collections
result1 = collection_client1.push
@@ -159,7 +160,7 @@ class SessionBasicTest < ActiveSupport::TestCase
assert_equal(result1, result2, 'check collections')
# check again after create
sleep 4
travel 4.seconds
result1 = collection_client1.push
assert(!result1, 'check collections - after create - recall')
result2 = collection_client2.push
@@ -168,7 +169,7 @@ class SessionBasicTest < ActiveSupport::TestCase
# change collection
group.destroy
sleep 4
travel 4.seconds
# get whole collections
result1 = collection_client1.push
@@ -178,12 +179,13 @@ class SessionBasicTest < ActiveSupport::TestCase
assert_equal(result1, result2, 'check collections')
# check again after destroy
sleep 4
travel 4.seconds
result1 = collection_client1.push
assert(!result1, 'check collections - after destroy - recall')
result2 = collection_client2.push
assert(!result2, 'check collections - after destroy - recall')
assert_equal(result1, result2, 'check collections')
travel_back
end
test 'c activity stream' do
@@ -206,30 +208,39 @@ class SessionBasicTest < ActiveSupport::TestCase
agent1.roles = roles
assert(agent1.save, 'create/update agent1')
# create min. on activity record
random_name = "Random:#{rand(9_999_999_999)}"
Group.create_or_update(
name: random_name,
updated_by_id: 1,
created_by_id: 1,
)
as_client1 = Sessions::Backend::ActivityStream.new(agent1, {}, false, '123-1', 3)
# get as stream
result1 = as_client1.push
assert(result1, 'check as agent1')
sleep 1
travel 1.second
# next check should be empty
result1 = as_client1.push
assert(!result1, 'check as agent1 - recall')
# next check should be empty
sleep 4
travel 4.seconds
result1 = as_client1.push
assert(!result1, 'check as agent1 - recall 2')
agent1.update_attribute(:email, 'activity-stream-agent11@example.com')
ticket = Ticket.create(title: '12323', group_id: 1, priority_id: 1, state_id: 1, customer_id: 1)
sleep 4
travel 4.seconds
# get as stream
result1 = as_client1.push
assert( result1, 'check as agent1 - recall 3')
travel_back
end
test 'c ticket_create' do
@@ -254,11 +265,12 @@ class SessionBasicTest < ActiveSupport::TestCase
Group.create(name: "SomeTicketCreateGroup::#{rand(999_999)}", active: true)
sleep 4
travel 4.seconds
# get as stream
result1 = ticket_create_client1.push
assert(result1, 'check ticket_create - recall 3')
travel_back
end
end

View File

@@ -38,17 +38,17 @@ class SessionBasicTicketTest < ActiveSupport::TestCase
assert(!result1, 'check ticket_overview_index - recall')
# next check should be empty / no changes
sleep 3
travel 3.seconds
result1 = client1.push
assert(!result1, 'check ticket_overview_index - recall 2')
# create ticket
ticket = Ticket.create(title: '12323', group_id: 1, priority_id: 1, state_id: 1, customer_id: 1)
sleep 3
travel 3.seconds
result1 = client1.push
assert(result1, 'check ticket_overview_index - recall 3')
sleep 3
travel 3.seconds
# chnage overview
overviews = Ticket::Overviews.all(
@@ -70,6 +70,7 @@ class SessionBasicTicketTest < ActiveSupport::TestCase
Sessions::Backend::TicketOverviewList.reset(user.id)
result1 = client1.push
assert(result1, 'check ticket_overview_index - recall 7')
travel_back
end

View File

@@ -67,7 +67,7 @@ class SessionCollectionsTest < ActiveSupport::TestCase
assert(check_if_collection_exists(result1, :Role), 'check collections - after init')
assert(check_if_collection_exists(result1, :Signature), 'check collections - after init')
assert(check_if_collection_exists(result1, :EmailAddress), 'check collections - after init')
sleep 1
travel 1.second
result2 = collection_client2.push
assert(result2, 'check collections')
assert(check_if_collection_exists(result2, :Group), 'check collections - after init')
@@ -108,7 +108,7 @@ class SessionCollectionsTest < ActiveSupport::TestCase
# change collection
group = Group.first
group.touch
sleep 3
travel 3.seconds
# get whole collections
result1 = collection_client1.push
@@ -130,6 +130,7 @@ class SessionCollectionsTest < ActiveSupport::TestCase
assert(result2.empty?, 'check collections - recall')
result3 = collection_client3.push
assert(result3.empty?, 'check collections - recall')
travel_back
end
def check_if_collection_exists(results, collection, attributes = nil)

View File

@@ -242,17 +242,17 @@ class SessionEnhancedTest < ActiveSupport::TestCase
'Group' => true,
'User' => nil,
}
check_if_collection_reset_message_exists(client_id1_0, collections, 'init')
check_if_collection_reset_message_exists(client_id1_1, collections, 'init')
check_if_collection_reset_message_exists(client_id2, collections, 'init')
assert_if_collection_reset_message_exists(client_id1_0, collections, 'init')
assert_if_collection_reset_message_exists(client_id1_1, collections, 'init')
assert_if_collection_reset_message_exists(client_id2, collections, 'init')
collections = {
'Group' => nil,
'User' => nil,
}
check_if_collection_reset_message_exists(client_id1_0, collections, 'init2')
check_if_collection_reset_message_exists(client_id1_1, collections, 'init2')
check_if_collection_reset_message_exists(client_id2, collections, 'init2')
assert_if_collection_reset_message_exists(client_id1_0, collections, 'init2')
assert_if_collection_reset_message_exists(client_id1_1, collections, 'init2')
assert_if_collection_reset_message_exists(client_id2, collections, 'init2')
sleep 8
@@ -260,9 +260,9 @@ class SessionEnhancedTest < ActiveSupport::TestCase
'Group' => nil,
'User' => nil,
}
check_if_collection_reset_message_exists(client_id1_0, collections, 'init3')
check_if_collection_reset_message_exists(client_id1_1, collections, 'init3')
check_if_collection_reset_message_exists(client_id2, collections, 'init3')
assert_if_collection_reset_message_exists(client_id1_0, collections, 'init3')
assert_if_collection_reset_message_exists(client_id1_1, collections, 'init3')
assert_if_collection_reset_message_exists(client_id2, collections, 'init3')
# change collection
group = Group.first
@@ -275,9 +275,9 @@ class SessionEnhancedTest < ActiveSupport::TestCase
'Group' => true,
'User' => nil,
}
check_if_collection_reset_message_exists(client_id1_0, collections, 'update')
check_if_collection_reset_message_exists(client_id1_1, collections, 'update')
check_if_collection_reset_message_exists(client_id2, collections, 'update')
assert_if_collection_reset_message_exists(client_id1_0, collections, 'update')
assert_if_collection_reset_message_exists(client_id1_1, collections, 'update')
assert_if_collection_reset_message_exists(client_id2, collections, 'update')
# check if session still exists after idle cleanup
sleep 4
@@ -290,7 +290,7 @@ class SessionEnhancedTest < ActiveSupport::TestCase
end
def check_if_collection_reset_message_exists(client_id, collections_orig, type)
def assert_if_collection_reset_message_exists(client_id, collections_orig, type)
messages = Sessions.queue(client_id)
#puts "cid: #{client_id}"
#puts "m: #{messages.inspect}"
@@ -309,7 +309,7 @@ class SessionEnhancedTest < ActiveSupport::TestCase
}
#puts "c: #{collections_result.inspect}"
collections_orig.each { |key, _value|
assert_equal( collections_orig[key], collections_result[key], "collection message for #{key} #{type}-check (client_id #{client_id})" )
assert_equal(collections_orig[key], collections_result[key], "collection message for #{key} #{type}-check (client_id #{client_id})")
}
end
end

View File

@@ -184,7 +184,7 @@ class TagTest < ActiveSupport::TestCase
updated_by_id: 1,
created_by_id: 1,
)
sleep 2
travel 2.seconds
Tag.tag_add(
object: 'Ticket',
o_id: ticket1.id,
@@ -266,7 +266,7 @@ class TagTest < ActiveSupport::TestCase
assert(tags_ticket2.include?('some tag4'))
# rename tag
sleep 2
travel 2.seconds
tag_item3 = Tag::Item.find_by(name: 'some tag3')
Tag::Item.rename(
id: tag_item3.id,
@@ -300,7 +300,7 @@ class TagTest < ActiveSupport::TestCase
assert(tags_ticket2.include?('some tag4'))
# merge tags
sleep 2
travel 2.seconds
Tag::Item.rename(
id: tag_item3.id,
name: 'some tag2',
@@ -333,7 +333,7 @@ class TagTest < ActiveSupport::TestCase
assert_not(Tag::Item.find_by(id: tag_item3.id))
# remove tag item
sleep 2
travel 2.seconds
tag_item4 = Tag::Item.find_by(name: 'some TAG4')
Tag::Item.remove(tag_item4.id)
@@ -359,6 +359,6 @@ class TagTest < ActiveSupport::TestCase
assert_not_equal(ticket1_lookup4.updated_at.to_s, ticket1_lookup3.updated_at.to_s)
ticket2_lookup4 = Ticket.lookup(id: ticket2.id)
assert_not_equal(ticket2_lookup4.updated_at.to_s, ticket2_lookup3.updated_at.to_s)
travel_back
end
end

View File

@@ -8,6 +8,14 @@ class TicketOverviewTest < ActiveSupport::TestCase
customer1 = nil
customer2 = nil
customer3 = nil
overview1 = nil
overview2 = nil
overview3 = nil
overview4 = nil
overview5 = nil
overview6 = nil
overview7 = nil
overview8 = nil
test 'aaa - setup' do
# create base
@@ -93,7 +101,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
Overview.destroy_all
UserInfo.current_user_id = 1
overview_role = Role.find_by(name: 'Agent')
Overview.create_or_update(
overview1 = Overview.create_or_update(
name: 'My assigned Tickets',
link: 'my_assigned',
prio: 1000,
@@ -120,7 +128,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
},
)
Overview.create_or_update(
overview2 = Overview.create_or_update(
name: 'Unassigned & Open',
link: 'all_unassigned',
prio: 1010,
@@ -146,7 +154,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
view_mode_default: 's',
},
)
Overview.create_or_update(
overview3 = Overview.create_or_update(
name: 'My Tickets 2',
link: 'my_tickets_2',
prio: 1020,
@@ -173,9 +181,36 @@ class TicketOverviewTest < ActiveSupport::TestCase
view_mode_default: 's',
},
)
overview4 = Overview.create_or_update(
name: 'My Tickets only with Note',
link: 'my_tickets_onyl_with_note',
prio: 1030,
role_id: overview_role.id,
user_ids: [agent1.id],
condition: {
'article.type_id' => {
operator: 'is',
value: Ticket::Article::Type.find_by(name: 'note').id,
},
'ticket.owner_id' => {
operator: 'is',
pre_condition: 'current_user.id',
},
},
order: {
by: 'created_at',
direction: 'ASC',
},
view: {
d: %w(title customer group created_at),
s: %w(title customer group created_at),
m: %w(number title customer group created_at),
view_mode_default: 's',
},
)
overview_role = Role.find_by(name: 'Customer')
Overview.create_or_update(
overview5 = Overview.create_or_update(
name: 'My Tickets',
link: 'my_tickets',
prio: 1100,
@@ -201,7 +236,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
view_mode_default: 's',
},
)
Overview.create_or_update(
overview6 = Overview.create_or_update(
name: 'My Organization Tickets',
link: 'my_organization_tickets',
prio: 1200,
@@ -228,7 +263,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
view_mode_default: 's',
},
)
Overview.create_or_update(
overview7 = Overview.create_or_update(
name: 'My Organization Tickets (open)',
link: 'my_organization_tickets_open',
prio: 1200,
@@ -258,7 +293,7 @@ class TicketOverviewTest < ActiveSupport::TestCase
)
overview_role = Role.find_by(name: 'Admin')
Overview.create_or_update(
overview8 = Overview.create_or_update(
name: 'Not Shown Admin',
link: 'not_shown_admin',
prio: 9900,
@@ -282,14 +317,15 @@ class TicketOverviewTest < ActiveSupport::TestCase
)
end
test 'ticket create' do
test 'bbb overiview index' do
result = Ticket::Overviews.all(
current_user: agent1,
)
assert_equal(2, result.count)
assert_equal(3, result.count)
assert_equal('My assigned Tickets', result[0].name)
assert_equal('Unassigned & Open', result[1].name)
assert_equal('My Tickets only with Note', result[2].name)
result = Ticket::Overviews.all(
current_user: agent2,
@@ -322,4 +358,504 @@ class TicketOverviewTest < ActiveSupport::TestCase
end
test 'ccc overiview content' do
Ticket.destroy_all
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
assert_equal(result[2][:count], 0)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
ticket1 = Ticket.create(
title: 'overview test 1',
group: Group.lookup(name: 'OverviewTest'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article1 = Ticket::Article.create(
ticket_id: ticket1.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message... 123',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket1.id)
assert_equal(result[1][:count], 1)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
assert_equal(result[2][:count], 0)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
travel 1.second # because of mysql millitime issues
ticket2 = Ticket.create(
title: 'overview test 2',
group: Group.lookup(name: 'OverviewTest'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '3 high'),
updated_by_id: 1,
created_by_id: 1,
)
article2 = Ticket::Article.create(
ticket_id: ticket2.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message... 123',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
type: Ticket::Article::Type.find_by(name: 'note'),
updated_by_id: 1,
created_by_id: 1,
)
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket1.id)
assert_equal(result[1][:tickets][1][:id], ticket2.id)
assert_equal(result[1][:count], 2)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
assert_equal(result[2][:count], 0)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
ticket2.owner_id = agent1.id
ticket2.save!
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[0][:tickets][0][:id], ticket2.id)
assert_equal(result[0][:count], 1)
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket1.id)
assert_equal(result[1][:count], 1)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert_equal(result[2][:tickets][0][:id], ticket2.id)
assert_equal(result[2][:count], 1)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
travel 1.second # because of mysql millitime issues
ticket3 = Ticket.create(
title: 'overview test 3',
group: Group.lookup(name: 'OverviewTest'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '1 low'),
updated_by_id: 1,
created_by_id: 1,
)
article3 = Ticket::Article.create(
ticket_id: ticket3.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message... 123',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
travel_back
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[0][:tickets][0][:id], ticket2.id)
assert_equal(result[0][:count], 1)
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket1.id)
assert_equal(result[1][:tickets][1][:id], ticket3.id)
assert_equal(result[1][:count], 2)
assert_equal(result[2][:overview][:id], overview4.id)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert_equal(result[2][:tickets][0][:id], ticket2.id)
assert_equal(result[2][:count], 1)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:id], overview3.id)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
overview2.order = {
by: 'created_at',
direction: 'DESC',
}
overview2.save!
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[0][:tickets][0][:id], ticket2.id)
assert_equal(result[0][:count], 1)
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket3.id)
assert_equal(result[1][:tickets][1][:id], ticket1.id)
assert_equal(result[1][:count], 2)
assert_equal(result[2][:overview][:id], overview4.id)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert_equal(result[2][:tickets][0][:id], ticket2.id)
assert_equal(result[2][:count], 1)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:id], overview3.id)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
overview2.order = {
by: 'priority_id',
direction: 'DESC',
}
overview2.save!
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[0][:tickets][0][:id], ticket2.id)
assert_equal(result[0][:count], 1)
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket1.id)
assert_equal(result[1][:tickets][1][:id], ticket3.id)
assert_equal(result[1][:count], 2)
assert_equal(result[2][:overview][:id], overview4.id)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert_equal(result[2][:tickets][0][:id], ticket2.id)
assert_equal(result[2][:count], 1)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:id], overview3.id)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
overview2.order = {
by: 'priority_id',
direction: 'ASC',
}
overview2.save!
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[0][:tickets][0][:id], ticket2.id)
assert_equal(result[0][:count], 1)
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket3.id)
assert_equal(result[1][:tickets][1][:id], ticket1.id)
assert_equal(result[1][:count], 2)
assert_equal(result[2][:overview][:id], overview4.id)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert_equal(result[2][:tickets][0][:id], ticket2.id)
assert_equal(result[2][:count], 1)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:id], overview3.id)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
overview2.order = {
by: 'priority',
direction: 'DESC',
}
overview2.save!
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[0][:tickets][0][:id], ticket2.id)
assert_equal(result[0][:count], 1)
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket1.id)
assert_equal(result[1][:tickets][1][:id], ticket3.id)
assert_equal(result[1][:count], 2)
assert_equal(result[2][:overview][:id], overview4.id)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert_equal(result[2][:tickets][0][:id], ticket2.id)
assert_equal(result[2][:count], 1)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:id], overview3.id)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
overview2.order = {
by: 'priority',
direction: 'ASC',
}
overview2.save!
result = Ticket::Overviews.index(agent1)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[0][:tickets][0][:id], ticket2.id)
assert_equal(result[0][:count], 1)
assert_equal(result[0][:tickets].class, Array)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert_not(result[1][:tickets].empty?)
assert_equal(result[1][:tickets][0][:id], ticket3.id)
assert_equal(result[1][:tickets][1][:id], ticket1.id)
assert_equal(result[1][:count], 2)
assert_equal(result[2][:overview][:id], overview4.id)
assert_equal(result[2][:overview][:name], 'My Tickets only with Note')
assert_equal(result[2][:overview][:view], 'my_tickets_onyl_with_note')
assert_equal(result[2][:tickets].class, Array)
assert_equal(result[2][:tickets][0][:id], ticket2.id)
assert_equal(result[2][:count], 1)
result = Ticket::Overviews.index(agent2)
assert_equal(result[0][:overview][:id], overview1.id)
assert_equal(result[0][:overview][:name], 'My assigned Tickets')
assert_equal(result[0][:overview][:view], 'my_assigned')
assert_equal(result[0][:count], 0)
assert_equal(result[0][:tickets].class, Array)
assert(result[0][:tickets].empty?)
assert_equal(result[1][:overview][:id], overview2.id)
assert_equal(result[1][:overview][:name], 'Unassigned & Open')
assert_equal(result[1][:overview][:view], 'all_unassigned')
assert_equal(result[1][:tickets].class, Array)
assert(result[1][:tickets].empty?)
assert_equal(result[1][:count], 0)
assert_equal(result[2][:overview][:id], overview3.id)
assert_equal(result[2][:overview][:name], 'My Tickets 2')
assert_equal(result[2][:overview][:view], 'my_tickets_2')
assert_equal(result[2][:tickets].class, Array)
assert(result[2][:tickets].empty?)
end
end

View File

@@ -90,7 +90,7 @@ class TicketRefObjectTouchTest < ActiveSupport::TestCase
assert(false, 'organization1.updated_at has not been updated')
end
sleep 4
travel 4.seconds
delete = ticket.destroy
assert(delete, 'ticket destroy')
@@ -109,11 +109,12 @@ class TicketRefObjectTouchTest < ActiveSupport::TestCase
else
assert(false, 'organization1.updated_at has not been updated')
end
travel_back
end
test 'c - check if customer (not organization) has been updated' do
sleep 6
travel 8.seconds
ticket = Ticket.create(
title: "some title2\n äöüß",
group: Group.lookup(name: 'Users'),
@@ -143,7 +144,7 @@ class TicketRefObjectTouchTest < ActiveSupport::TestCase
assert(true, 'organization1.updated_at has not been updated')
end
sleep 4
travel 4.seconds
delete = ticket.destroy
assert(delete, 'ticket destroy')
@@ -162,6 +163,6 @@ class TicketRefObjectTouchTest < ActiveSupport::TestCase
else
assert(true, 'organization1.updated_at has not been updated')
end
travel_back
end
end

View File

@@ -100,7 +100,7 @@ class TicketSelectorTest < ActiveSupport::TestCase
assert(ticket1, 'ticket created')
assert_equal(ticket1.customer.id, customer1.id)
assert_equal(ticket1.organization.id, organization1.id)
sleep 1
travel 1.second
ticket2 = Ticket.create!(
title: 'some title2',
@@ -116,7 +116,7 @@ class TicketSelectorTest < ActiveSupport::TestCase
assert(ticket2, 'ticket created')
assert_equal(ticket2.customer.id, customer2.id)
assert_equal(ticket2.organization_id, nil)
sleep 1
travel 1.second
ticket3 = Ticket.create!(
title: 'some title3',
@@ -133,7 +133,7 @@ class TicketSelectorTest < ActiveSupport::TestCase
assert(ticket3, 'ticket created')
assert_equal(ticket3.customer.id, customer2.id)
assert_equal(ticket3.organization_id, nil)
sleep 1
travel 1.second
# search not matching
condition = {
@@ -995,7 +995,7 @@ class TicketSelectorTest < ActiveSupport::TestCase
ticket_count, tickets = Ticket.selectors(condition, 10)
assert_equal(ticket_count, 0)
travel_back
end
end

View File

@@ -43,7 +43,7 @@ class TicketTest < ActiveSupport::TestCase
assert_equal(ticket.close_at, nil, 'ticket.close_at verify - inbound')
# create inbound article #2
sleep 2
travel 2.seconds
article_inbound2 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
@@ -91,7 +91,7 @@ class TicketTest < ActiveSupport::TestCase
assert_equal(ticket.close_at, nil, 'ticket.close_at verify - note')
# create outbound article
sleep 2
travel 2.seconds
article_outbound = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_recipient@example.com',
@@ -139,7 +139,7 @@ class TicketTest < ActiveSupport::TestCase
assert_equal(ticket.close_at, nil, 'ticket.close_at verify - outbound')
# create inbound article #4
sleep 2
travel 2.seconds
article_inbound4 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
@@ -189,10 +189,32 @@ class TicketTest < ActiveSupport::TestCase
ticket = Ticket.find(ticket.id)
assert_equal(ticket.state.name, 'closed', 'state verify')
assert_equal(ticket.pending_time, nil )
assert_equal(ticket.pending_time, nil)
# delete article
article_note = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some person',
subject: 'some note',
body: 'some message',
internal: true,
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
type: Ticket::Article::Type.find_by(name: 'note'),
updated_by_id: 1,
created_by_id: 1,
)
ticket = Ticket.find(ticket.id)
assert_equal(ticket.article_count, 7, 'ticket.article_count verify - note')
article_note.destroy
ticket = Ticket.find(ticket.id)
assert_equal(ticket.article_count, 6, 'ticket.article_count verify - note')
delete = ticket.destroy
assert(delete, 'ticket destroy')
travel_back
end
test 'ticket latest change' do
@@ -207,7 +229,7 @@ class TicketTest < ActiveSupport::TestCase
)
assert_equal(Ticket.latest_change.to_s, ticket1.updated_at.to_s)
sleep 1
travel 1.minute
ticket2 = Ticket.create(
title: 'latest change 2',
@@ -220,20 +242,21 @@ class TicketTest < ActiveSupport::TestCase
)
assert_equal(Ticket.latest_change.to_s, ticket2.updated_at.to_s)
sleep 1
travel 1.minute
ticket1.title = 'latest change 1 - 1'
ticket1.save
assert_equal(Ticket.latest_change.to_s, ticket1.updated_at.to_s)
sleep 1
travel 1.minute
ticket1.touch
assert_equal(Ticket.latest_change.to_s, ticket1.updated_at.to_s)
ticket1.destroy
assert_equal(Ticket.latest_change.to_s, ticket2.updated_at.to_s)
ticket2.destroy
travel_back
end
test 'ticket process_pending' do

View File

@@ -0,0 +1,185 @@
# encoding: utf-8
require 'test_helper'
class TicketXssTest < ActiveSupport::TestCase
test 'xss via model' do
ticket = Ticket.create(
title: 'test 123 <script type="text/javascript">alert("XSS!");</script>',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
assert(ticket, 'ticket created')
assert_equal('test 123 <script type="text/javascript">alert("XSS!");</script>', ticket.title, 'ticket.title verify')
assert_equal('Users', ticket.group.name, 'ticket.group verify')
assert_equal('new', ticket.state.name, 'ticket.state verify')
article1 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: '<script type="text/javascript">alert("XSS!");</script>',
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,
)
assert_equal('alert("XSS!");', article1.body, 'article1.body verify - inbound')
article2 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'please tell me this doesn\'t work: <script type="text/javascript">alert("XSS!");</script>',
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,
)
assert_equal('please tell me this doesn\'t work: alert("XSS!");', article2.body, 'article2.body verify - inbound')
article3 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'please tell me this doesn\'t work: <table>ada<tr></tr></table><div class="adasd" id="123" data-abc="123"></div><div><a href="javascript:someFunction()">LINK</a><a href="http://lalal.de">aa</a><some_not_existing>ABC</some_not_existing>',
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,
)
assert_equal("please tell me this doesn't work: <table>ada<tr></tr>
</table><div class=\"adasd\"></div><div>
<a>LINK</a><a href=\"http://lalal.de\" rel=\"nofollow\" target=\"_blank\">aa</a>ABC</div>", article3.body, 'article3.body verify - inbound')
article4 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'please tell me this doesn\'t work: <video>some video</video><foo>alal</foo>',
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,
)
assert_equal("please tell me this doesn't work: <video>some video</video>alal", article4.body, 'article4.body verify - inbound')
article5 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/plain',
body: 'please tell me this doesn\'t work: <table>ada<tr></tr></table><div class="adasd" id="123" data-signature-id="123"></div><div><a href="javascript:someFunction()">LINK</a><a href="http://lalal.de">aa</a><some_not_existing>ABC</some_not_existing>',
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,
)
assert_equal('please tell me this doesn\'t work: <table>ada<tr></tr></table><div class="adasd" id="123" data-signature-id="123"></div><div><a href="javascript:someFunction()">LINK</a><a href="http://lalal.de">aa</a><some_not_existing>ABC</some_not_existing>', article5.body, 'article5.body verify - inbound')
article6 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'some message article helper test1 <div><img style="width: 85.5px; height: 49.5px" src="cid:15.274327094.140938@zammad.example.com">asdasd<img src="cid:15.274327094.140939@zammad.example.com"><br>',
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,
)
assert_equal('some message article helper test1 <div>
<img style="width: 85.5px; height: 49.5px;" src="cid:15.274327094.140938@zammad.example.com">asdasd<img src="cid:15.274327094.140939@zammad.example.com"><br>
</div>', article6.body, 'article6.body verify - inbound')
article7 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'some message article helper test1 <div><img style="width: 85.5px; height: 49.5px" src="api/v1/ticket_attachment/123/123/123">asdasd<img src="api/v1/ticket_attachment/123/123/123"><br>',
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,
)
assert_equal('some message article helper test1 <div>
<img style="width: 85.5px; height: 49.5px;" src="api/v1/ticket_attachment/123/123/123">asdasd<img src="api/v1/ticket_attachment/123/123/123"><br>
</div>', article7.body, 'article7.body verify - inbound')
article8 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'some message article helper test1 <a href="#" onclick="some_function();">abc</a> <a href="https://example.com" oNclIck="some_function();">123</a><body>123</body>',
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,
)
assert_equal('some message article helper test1 <a href="#">abc</a> <a href="https://example.com" rel="nofollow" target="_blank">123</a>123', article8.body, 'article8.body verify - inbound')
end
test 'xss via mail' do
data = 'From: ME Bob <me@example.com>
To: customer@example.com
Subject: some subject
Content-Type: text/html
MIME-Version: 1.0
no HTML <script type="text/javascript">alert(\'XSS\')</script>'
parser = Channel::EmailParser.new
ticket, article, user = parser.process({}, data)
assert_equal('text/html', ticket.articles.first.content_type)
assert_equal('no HTML alert(\'XSS\')', ticket.articles.first.body)
data = 'From: ME Bob <me@example.com>
To: customer@example.com
Subject: some subject
Content-Type: text/plain
MIME-Version: 1.0
no HTML <script type="text/javascript">alert(\'XSS\')</script>'
parser = Channel::EmailParser.new
ticket, article, user = parser.process({}, data)
assert_equal('text/plain', ticket.articles.first.content_type)
assert_equal('no HTML <script type="text/javascript">alert(\'XSS\')</script>', ticket.articles.first.body)
end
end