Compare commits

...

254 Commits

Author SHA1 Message Date
Florian Liebe
3c04934046 Release Zammad 7.0.3. 🚀 2026-06-25 07:26:26 +02:00
Florian Liebe
936403b2db Maintenance: Update concurrent-ruby to 1.3.7. 2026-06-23 08:15:11 +02:00
Rolf Schmidt
599144afb4 Maintenance: Update faraday to 2.14.3. 2026-06-22 22:21:52 +02:00
Rene Reimann
cf3425712e Maintenance: Improve validation of client identifiers in session handling.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-06-22 10:49:27 +02:00
Florian Liebe
5e6e966599 Maintenance: Update nokogiri to 1.19.4. 2026-06-22 08:35:48 +02:00
Florian Liebe
c2bba6fdc0 Maintenance: Trigger build for packager.io. 2026-06-17 07:33:35 +02:00
Florian Liebe
9dc15acf23 Release Zammad 7.0.2. 🚀 2026-06-17 06:05:52 +02:00
Florian Liebe
e50ef69bea Follow up f059eb2d98 - Maintenance: Improve user context with token handling. 2026-06-16 08:06:36 +02:00
Rene Reimann
e661d00efb Maintenance: Improve ticket attachment download endpoint. 2026-06-15 11:03:04 +02:00
Rene Reimann
b225881b9a Maintenance: Improve long polling controller. 2026-06-15 11:03:04 +02:00
Mantas Masalskis
13ccc136e5 Maintenance: Improve KB attachments handling 2026-06-15 11:03:04 +02:00
Florian Liebe
a04a95b694 Maintenance: Improve OTRS import controller. 2026-06-15 11:03:04 +02:00
Florian Liebe
4a9dd5f8a8 Maintenance: Improve handling of PGP signature verifying. 2026-06-15 11:03:04 +02:00
Rene Reimann
3f6e8a5fa3 Maintenance: Improve HTML sanitizer to remove unsafe Zammad API paths. 2026-06-15 11:03:04 +02:00
Rene Reimann
4ccd5d5730 Maintenance: Improve taskbar handling in GraphQL context. 2026-06-15 11:03:04 +02:00
Florian Liebe
c767f32c00 Maintenance: Deduplicate issue tracker controller code. 2026-06-15 11:03:04 +02:00
Florian Liebe
b059bd1cac Maintenance: Improve knowledge base answer suggestion transforming.
(cherry picked from commit f2b16d683c8ada4d831a68b50d091a02fee37245)

ee268f1a Maintenance: Improve knowlege base answer suggestion transforming.

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-06-15 11:03:04 +02:00
Rene Reimann
1fae97d924 Maintenance: Improve fetching of avatars. 2026-06-15 11:03:04 +02:00
Dominik Klein
61d2c42696 Maintenance: Desktop view - Improve online notification output in popover. 2026-06-15 11:03:04 +02:00
Mantas Masalskis
7b6c0adec0 Maintenance: Improve Knowledge Base search when using granular permissions 2026-06-15 11:03:04 +02:00
Florian Liebe
f059eb2d98 Maintenance: Improve user context with token handling.
Co-authored-by: Rene Reimann <rr@zammad.com>
2026-06-15 11:03:04 +02:00
Florian Liebe
02c1ff8dc6 Maintenance: Improve HTML sanitizer to remove unsafe srcset entries. 2026-06-15 11:03:04 +02:00
Florian Liebe
3cdf1b08a0 Maintenance: Update net-imap to 0.6.4.1. 2026-06-15 07:16:23 +02:00
Mantas Masalskis
0fa7d753b0 Fixes #6197 - Checklist template item order is not preserved when creating a... 2026-06-12 17:49:00 +03:00
Dusan Vuckovic
f87b42752e Follow-up b2cfa6f350 - Fixes #6159 - Note fields accept and display HTML tags. 2026-06-10 11:45:47 +02:00
Florian Liebe
b38ecfe2ef Maintenance: Update oauth to 1.1.6, oauth2 to 2.0.22. 2026-06-10 10:07:16 +02:00
Benjamin Scharf
4a373e663e Fixes #6184 - Public links do not get translated
Co-authored-by: Benjamin Scharf <bs@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-06-10 08:07:12 +02:00
Dusan Vuckovic
b2cfa6f350 Fixes #6159 - Note fields accept and display HTML tags. 2026-06-09 18:22:46 +01:00
Weblate
09b2a58786 Maintenance: Update translated files and catalog 2026-06-09 13:03:57 +01:00
Weblate
9afb6253ae Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Balázs Úr <balazs@urbalazs.hu>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Mantas Masalskis <mantas@idev.lt>
Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Murat Duman <mduman@metu.edu.tr>
Co-authored-by: Thomas Meijer <tvm@bizconnect.dk>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zen Ken <likemoue@gmail.com>
Co-authored-by: michal-zila <michal.zila@somi.sk>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/da/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hu/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/lt/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sk/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/tr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/vi/
Translation: Zammad/Zammad (stable version)
2026-06-09 11:58:05 +00:00
Florian Liebe
61708c75d0 Maintenance: Update puma to 7.2.1. 2026-06-07 14:14:11 +02:00
Florian Liebe
f86350e6f4 Fixes #6182 - Today button of date picker is not visible in Light mode. 2026-06-05 13:41:06 +02:00
Rolf Schmidt
ce735a391e Fixes #6179 - Race conditions on deleting many users via scheduler jobs.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-06-03 12:24:41 +02:00
Rolf Schmidt
dfb9e49bad Maintenance: Add missing backtrace for failing jobs.
Co-authored-by: Mantas Masalskis <mm@zammad.com>


(cherry picked from commit 5c9e76b7ef)

2d592762 Maintenance: Add missing backtrace for failing jobs.

Co-authored-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-06-03 10:53:21 +02:00
Dominik Klein
e917c72c99 Fixes #6174 - Webhook> Date attributes do not work in custom payloads. 2026-06-02 08:25:53 +02:00
Mantas Masalskis
67cd76bed7 Fixes #6134 - Year Report has no results for October in Europe/London timezone 2026-05-29 13:42:06 +03:00
Dominik Klein
ef127b2e18 Maintenance: Log error messages from microsoft graph inside the http log (for more insides). 2026-05-28 14:41:45 +02:00
Florian Liebe
a58794c228 Maintenance: Update gem dependencies. 2026-05-27 08:44:41 +02:00
Mantas Masalskis
52ce6eeb97 Fixes #6144 - ISO-2022-JP emails are not parsed correct if explicit charset is missing 2026-05-26 17:49:56 +03:00
Florian Liebe
b009f5eb72 Maintenance: Update graphql gem. 2026-05-26 08:11:37 +02:00
Dusan Vuckovic
e24b963134 Fixes #6148 - Creating root-level Knowledge Base category fails with 'Couldn't find KnowledgeBase::Category without an ID'. 2026-05-22 20:00:32 +03:00
Mantas Masalskis
488feb53ec Fixes #6141 - Email parsing crashes if content-type is malformed 2026-05-21 19:17:29 +03:00
Weblate
7d174a0e84 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/
Translation: Zammad/Zammad (stable version)
2026-05-20 22:02:32 +00:00
Weblate
a770125d42 Maintenance: Update translated files and catalog 2026-05-19 23:05:15 +01:00
Weblate
622f6eb0e1 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Alivion <dani-pranger@live.nl>
Co-authored-by: Anael Mobilia <contrib@anael.eu>
Co-authored-by: Andi Chandler <andi@gowling.com>
Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Balázs Úr <balazs@urbalazs.hu>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Eduardo Soares <edurbs@gmail.com>
Co-authored-by: FoW <fow@netspheres.org>
Co-authored-by: GoneTone <p29022716@gmail.com>
Co-authored-by: Josep Prada <josepprada@gmail.com>
Co-authored-by: Karoly Nagy <cpt.charlesg@gmail.com>
Co-authored-by: KoJie <jk@zammad.com>
Co-authored-by: MacaGrox <1695460597ab@gmail.com>
Co-authored-by: Mantas Masalskis <mantas@idev.lt>
Co-authored-by: Mantikor <nhyuga.r@gmail.com>
Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Murat Duman <mduman@metu.edu.tr>
Co-authored-by: Osmar Valdebenito <osmar.valdebenito@uchile.cl>
Co-authored-by: Paul van Wanrooij <pvwanrooij@hgvt.nl>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Random <random_r@users.noreply.translations.zammad.org>
Co-authored-by: TomAdriaensen <tom.adriaensen@hivset.be>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: albanobattistella <albanobattistella@gmail.com>
Co-authored-by: jason_k <iasonas2001@hotmail.com>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/ca/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/el/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/en_GB/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/es_MX/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/fr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hu/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/it/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/ko/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/lt/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/nl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/pt_BR/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/tr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/uk/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/zh_Hans/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/zh_Hant/
Translation: Zammad/Zammad (stable version)
2026-05-19 22:02:14 +00:00
Mantas Masalskis
6e54559043 Fixes #6135 - KB with granular permissions does not show top-level category 2026-05-19 19:19:00 +03:00
Mantas Masalskis
9f7ffd99d9 Fixes #5197 - Error on adding KB answers to category with granular permissions 2026-05-19 17:01:54 +03:00
Florian Liebe
6c48620951 Fixes #6115 - Header Based Actions not working in 7.0.
Co-authored-by: Rene Reimann <rr@zammad.com>
2026-05-19 15:33:45 +02:00
Mantas Masalskis
17c9d075e8 Fixes #6136 - Complex emails signature detection crashes email parsing 2026-05-18 10:03:09 +03:00
Mantas Masalskis
76f4f9157a Fixes #5965 - Sessions metadata is not cleaned up when using ZAMMAD_PROCESS_SESSIONS_JOBS_WORKERS 2026-05-15 00:11:55 +03:00
Mantas Masalskis
332bba5afd Maintenance: Bumped net-imap to 0.6.4 (CVE-2026-42246, CVE-2026-42256,... 2026-05-14 23:14:33 +03:00
Dominik Klein
7cd61fee9f Fixes #6127 - In desktop view ticket.* variables in signature resolve to "-"... 2026-05-13 21:02:30 +02:00
Florian Liebe
6e22cbb2cd Maintenance: Update gem dependencies. 2026-05-13 09:59:54 +02:00
Mantas Masalskis
d438e0a9db Follow up - 5c64cd47 - Fixes #6108 - Attachment file size is wrong in the public KB answer 2026-05-06 17:40:49 +03:00
Mantas Masalskis
2453ea2642 Fixes #6108 - Attachment file size is wrong in the public KB answer 2026-05-06 17:40:04 +03:00
Dominik Klein
2de9362f50 Fixes #6105 - Admin signshow / session bootstrap 500 when perform.ai.ai_agent.ai_agent_id is an Array (NoMethodError: to_i for Array) 2026-05-06 13:43:31 +02:00
Florian Liebe
57af4f9573 Maintenance: Update release script to properly create language list. 2026-05-06 09:44:21 +02:00
Rene Reimann
3b45354591 Fixes #6081 - Concurrent triggers are mixed in ticket history 2026-04-30 13:53:41 +02:00
Rolf Schmidt
9a75d3afc1 Fixes #6107 - Add possibility to force refresh browsers.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-04-29 13:17:56 +02:00
Rolf Schmidt
82dee637e7 Fixes #6097 - websocket fallback is leading to massive requests on long polling controller.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-04-29 12:17:44 +02:00
Florian Liebe
acbbc70745 Fixes #6102 - Custom OpenAI OCR builds invalid data URI. 2026-04-28 16:12:51 +02:00
Florian Liebe
1f68767300 Fixes #6054 - UndefinedConversionError for valid UTF-8 characters in GB2312-declared HTML emails. 2026-04-28 14:24:23 +02:00
Ben
78ad4e429d Fixes #6099 - Admin - Translation in French - bad escaping of quote characters 2026-04-28 09:13:20 +02:00
Ben
46e5d2e079 Fixes #6098 - Translation - Admin / "Knowledge Base" - missing translation 2026-04-27 15:25:00 +01:00
Ben
df77241aa1 Fixes #6093 - Inactive user cannot be data-privacy removed unless reactivated 2026-04-27 11:46:52 +01:00
Mantas Masalskis
5e40f9cb70 Fixes #6043 - Email channel fetching gets stuck 2026-04-27 11:56:20 +03:00
Joe Schröcker
d6167f95f5 Fixes #6071 - Ticket > Zoom > Sidebar > Tag: Delete icon disappears when name is too long 2026-04-27 09:47:17 +02:00
Florian Liebe
52a7fc39a9 Maintenance: Update erb gem. 2026-04-24 08:02:14 +02:00
Dominik Klein
4a8609d41f Maintenance: Disable showing comment when coderabbit review is skipped. 2026-04-23 20:27:05 +02:00
Florian Liebe
82f35c35f3 Maintenance: Improve packager.io testing for RHEL. 2026-04-20 12:05:44 +02:00
Rene Reimann
cbd48bd198 Fixes #5991 - Microsoft Graph OAuth callback fails due to timeouts 2026-04-17 16:43:49 +02:00
Dominik Klein
e0dddc16e3 Maintenance: Added coderabbit configuration for testing period. 2026-04-17 15:25:24 +02:00
Florian Liebe
1b6c4e9523 Fixes #6078 - Package installation not working out of the box on CentOS 9. 2026-04-17 11:20:38 +02:00
Dusan Vuckovic
8b800ddb29 Fixes #6077 - AI tokens usage with Azure AI. 2026-04-17 09:57:33 +01:00
Dusan Vuckovic
6b87aa9bbd Fixes #6084 - Provider settings disappear when switching back and forth.
Co-authored-by: Mantas Masalskis <mm@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-04-16 20:44:47 +01:00
Dusan Vuckovic
aa2a818162 Fixes #6060 - Missing temperature support by some models for some providers. 2026-04-15 15:57:30 +01:00
Dusan Vuckovic
c317f2a8cf Maintenance: Improve AI analytics usage recording error handling. 2026-04-15 11:21:00 +01:00
ggruening
bacc6a9e81 Fixes #6033 Closes #6053 - Initial backup process fails completely under certain circumstances in Docker context. 2026-04-14 14:04:03 +02:00
Florian Liebe
8003e5f178 Maintenance: Improve packager.io workflow. 2026-04-14 13:08:48 +02:00
Weblate
a7e3888aef Maintenance: Update translated files and catalog 2026-04-14 07:28:39 +01:00
Weblate
8f0b37bcd5 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Anael Mobilia <contrib@anael.eu>
Co-authored-by: Andi Chandler <andi@gowling.com>
Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Balázs Úr <balazs@urbalazs.hu>
Co-authored-by: CZAsterix <admin@upice-net.cz>
Co-authored-by: Colin Andries <colin.andries@bisdomantwerpen.be>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Florian Liebe <dev@florianlie.be>
Co-authored-by: Ginjeira <lmgl1711@gmail.com>
Co-authored-by: JackYeah <jack11070@gmail.com>
Co-authored-by: Karoly Nagy <cpt.charlesg@gmail.com>
Co-authored-by: Mantikor <nhyuga.r@gmail.com>
Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Murat Duman <mduman@metu.edu.tr>
Co-authored-by: NightFlexer <b111vitalii@gmail.com>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Random <random_r@users.noreply.translations.zammad.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: albanobattistella <albanobattistella@gmail.com>
Co-authored-by: mathew-uems <mathew.chen@edgentauems.com.tw>
Co-authored-by: paulotwo <paulotwo@gmail.com>
Co-authored-by: sigurbjartur <github@sigurbjartur.com>
Co-authored-by: tekvsakdan <boris.ivanovic@tekvsakdan.si>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/cs/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/en_GB/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/fr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hu/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/is/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/it/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/nl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/pt_BR/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/pt_PT/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/ru/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/tr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/uk/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/zh_Hans/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/zh_Hant/
Translation: Zammad/Zammad (stable version)
2026-04-14 06:24:41 +00:00
Florian Liebe
51e6b36ea9 Maintenance: Update used CA certificate for the LDAP container. 2026-04-14 08:23:16 +02:00
Rolf Schmidt
8a5307eba4 Maintenance: Make packages sorted in list and verify task.
(cherry picked from commit dc4a0f3956)

dc4a0f39 Maintenance: Make packages sorted in list and verify task.

Co-authored-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-04-13 15:52:42 +02:00
Rolf Schmidt
f75a86766c Maintenance: Add zammad:package:verify rake to check installation status of packages.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-04-13 15:30:08 +02:00
Rolf Schmidt
ea46e905a9 Fixes #6072 - ai_action field resolves into error on ticket get request.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-04-13 15:21:44 +02:00
Martin Gruner
a8a3058794 Maintenance: Add itvsh.de reference to publiccode.yml
(cherry picked from commit c8d9fce845)

Co-authored-by: Martin Gruner <mg@zammad.com>
2026-04-13 10:41:51 +02:00
Joe Schröcker
81b38d6a84 Fixes #6058 - Attachment delete icon disappears when filename is too long
Co-authored-by: Joe Schröcker js@zammad.com
2026-04-09 17:13:45 +02:00
Florian Liebe
d6c89953f3 Maintenance: Update meta information for openCode. 2026-04-08 08:58:11 +02:00
Florian Liebe
6a5ac49588 Release Zammad 7.0.1. 🚀 2026-04-08 05:13:27 +02:00
Mantas Masalskis
2f46ad76cf Maintenance: Improve link adding during ticket creation. 2026-04-07 08:27:25 +02:00
Mantas Masalskis
1e734d1ab6 Maintenance: Improves Webhook and External Data Source resilience against malicious endpoint addresses 2026-04-07 08:27:25 +02:00
Florian Liebe
c3cbedea23 Maintenance: Improve SSO mechanism. 2026-04-07 08:27:25 +02:00
Dominik Klein
16a4a2e1be Maintenance: Improve notification renderer template handling in some special situations. 2026-04-07 08:27:25 +02:00
Florian Liebe
f4bacb312e Maintenance: Harden external credentials flow.
(cherry picked from commit 5ae915aa3cf06fa4ca7d661affa001761a5cd956)

986579fb Maintenance: Harden external credentials flow.
4a258404 Tidy

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-04-07 08:27:25 +02:00
Florian Liebe
c3015bc4f9 Maintenance: Improve wipe mechanism of HTML sanitizer.
(cherry picked from commit 9d2a4ef96201e3127048f9145c3d11c0be298ffa)

9d2a4ef9 Maintenance: Improve wipe mechanism of HTML sanitizer.

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-04-07 08:27:25 +02:00
Florian Liebe
2ecf1e6812 Maintenance: Enhance authorization and refactor context generation for AI assistance controller.
(cherry picked from commit c59eaf15b42e0868c2d3ea18f0035812380b43fb)

1becd612 Maintenance: Enhance authorization and refactor context generation for AI assistance controller.
534d237b Add note.
fa2b3df2 Tidy

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-04-07 08:27:25 +02:00
Dominik Klein
0780538e04 Maintenance: Improve getting started handling.
Co-authored-by: Florian Liebe <fl@zammad.com>
Co-authored-by: Dominik Klein <dk@zammad.com>
2026-04-07 08:27:25 +02:00
Florian Liebe
a0a4a2b05e Maintenance: Update for Zammad 7.0.1. 2026-04-07 08:27:25 +02:00
Dusan Vuckovic
55467d2977 Maintenance: Stabilize shared drafts test. 2026-04-06 15:06:02 +01:00
Benjamin Scharf
e6ddc624c6 Maintenance: Desktop view - Fix warning for useThirdPartyAuthentication 2026-04-03 13:49:49 +01:00
Dominik Klein
ca817321ac Maintenance: Desktop View - Problems when value and show (with core workflow) is happening in the same update. 2026-04-02 22:34:41 +02:00
Florian Liebe
ee2b8445b7 Maintenance: Improve loading mechanism for aws-sdk-s3 gem.
Thanks to Andreas Schneider <asn@cryptomilk.org> for the idea.
2026-04-02 15:19:06 +02:00
Dusan Vuckovic
c414d7b2d8 Maintenance: Desktop view - Fix user popover placement in ticket subscribers. 2026-04-02 09:54:14 +01:00
Dominik Klein
8265be04d7 Maintenance: Desktop view - Improve form data default handling related to newly created object attributes. 2026-04-01 22:48:06 +02:00
Dominik Klein
d3a75b7a64 Maintenance: Desktop view - Fix problems with dirty state in different situations with the show/remove of fields. 2026-03-31 21:38:01 +02:00
Martin Gruner
730cd0c9ca Maintenance: Update nodejs buildpack for packager.io builds to v340
(cherry picked from commit 696534cd3c)

8c8cb702 Maintenance. Update nodejs buildpack for packager.io builds to v340
3a0d8ca1 Also trigger builds

Co-authored-by: Martin Gruner <mg@zammad.com>
2026-03-31 15:55:41 +02:00
Dusan Vuckovic
2de087fdb2 Maintenance: Image asset for forbidden errors is scaled in an unexpected way. 2026-03-30 15:31:31 +01:00
Dominik Klein
e8a57b6989 Fixes #6048 - Ticket changes from AI-Agents are not triggering the configured triggers. 2026-03-27 21:03:43 +01:00
Joe Schröcker
4251d17d39 Fixes #4990 - User not redirected properly after initial account verification 2026-03-27 17:03:21 +01:00
Benjamin Scharf
9b8f2722e6 Fixes #5930 - Hardcoded Ticket# instead of value from Ticket Hook and Ticket Hook Divider settings 2026-03-27 13:05:04 +00:00
Rolf Schmidt
7157e07858 Fixes #6049 - zammad update fails if packages have own gemfiles.
Co-authored-by: Martin Gruner <mg@zammad.com>
2026-03-27 12:34:53 +01:00
Mantas
59519b71aa Maintenance: Update Ruby on Rails to 8.0.5 2026-03-27 09:46:16 +02:00
Dominik Klein
4c3df3b227 Maintenance: Improve test service worker helper for new node versions. 2026-03-26 19:41:42 +01:00
Dusan Vuckovic
d1907eaacf Fixes #5934 - Ambiguous word "From" in language files. 2026-03-26 15:48:18 +00:00
Florian Liebe
a40c879deb Maintenance: Update icalendar gem. 2026-03-26 08:04:56 +01:00
Rolf Schmidt
bc1b294a01 Follow-up - cfeee1ef - Feature: Desktop view - Add recently closed list (instead of current recently viewed implementation).
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-03-25 14:53:33 +01:00
Rolf Schmidt
dfec1746e3 Fixes #6038 - Objects of the format External data source field cannot be set as mandatory.
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-03-25 12:44:33 +01:00
Florian Liebe
650585fa7a Maintenance: Update json gem. 2026-03-25 07:48:23 +01:00
Simon Bärlocher
f782bb8090 Fixes #6036 - GitLab integration not working after update to 18.10.
Closes #6037 - Accept work_items URLs in addition to issues URLs.
2026-03-24 20:28:56 +01:00
Dominik Klein
6ef1671c0e Fixes #6029 - WhatsApp channel can not be changed without entering credentials... 2026-03-24 17:46:10 +01:00
Dominik Klein
fea86c2a9f Fixes #2319 - Replying to your own articles (agent perspective) or other situations does not generate a signature on reply 2026-03-24 17:31:49 +01:00
Benjamin Scharf
b82372af1a Maintenance: Content in pre tags is cut off 2026-03-24 07:31:01 +00:00
Joe Schroecker
e0d5fd3a3f Fixes #5053 - Settings>Security>Passwords values not casted properly when changed via API
Co-authored-by: Mantas Masalskis <mm@zammad.com>
Co-authored-by: Joe Schroecker <js@zammad.com>
2026-03-23 14:44:46 +01:00
Martin Gruner
c0c8a40738 Maintenance: Add reference customer to publiccode.yml 2026-03-23 07:38:34 +01:00
Dominik Klein
b35be827b0 Fixes #5855 - Ticket sidebar form field value is flickering when updating the ticket. 2026-03-20 19:54:30 +01:00
Dusan Vuckovic
8a2664c9d1 Fixes #6006 - Opening a ticket link on the phone with an SSO authentication takes you to the dashboard instead of the ticket. 2026-03-20 13:53:02 +00:00
Dusan Vuckovic
3edeb40f6d Follow-up bc615144ae - Maintenance: Improve error handling when sending BETA UI feedback. 2026-03-19 14:04:59 +00:00
Rolf Schmidt
c598c8095b Maintenance: Make sure that caches are cleared as well for delayed jobs.
Co-authored-by: Mantas Masalskis <mm@zammad.com>
2026-03-19 13:30:05 +01:00
Rolf Schmidt
ecead9532f Fixes #6023 - Add rake tasks to inspect and retry failed data privacy tasks.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-03-19 13:28:40 +01:00
Dusan Vuckovic
bc615144ae Maintenance: Improve error handling when sending BETA UI feedback.
Co-authored-by: Dominik Klein <dk@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-03-19 11:02:40 +00:00
Dominik Klein
fd06a330c1 Maintenance: Move from prettier to oxfmt as our formatter. 2026-03-18 22:58:14 +01:00
Dusan Vuckovic
02e28071d3 Fixes #6018 - Text formatting bubble menu disappears when using Ctrl+Shift+arrows for text selecting. 2026-03-18 15:58:45 +00:00
Dominik Klein
276496e637 Fixes #6014 - Missing category when searching for existing knowledge base articles in the UI 2026-03-18 12:00:53 +01:00
Rolf Schmidt
5b45d98881 Fixes #6016 - DataPrivacyTask failed because of AI relations.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-03-17 13:48:41 +01:00
Rene Reimann
5235f5cb9b Fixes #6013 - Hard-coded "Clone:" name prefix for cloned objects. 2026-03-17 13:07:56 +01:00
Dominik Klein
81e67d9139 Fixes #6020 - AI Ticket Summary sometimes fails to return the conversation summary as an list of paragraphs 2026-03-17 10:24:13 +01:00
Rene Reimann
10facc67f1 Fixes #3761 - Oversized-mail sender not properly formatted. 2026-03-17 09:22:10 +01:00
Dusan Vuckovic
ac644a7bb3 Fixes #6009 - Zammad backup docker image error with postgresql version 18+. 2026-03-16 14:05:26 +00:00
Rolf Schmidt
076aa21b3a Maintenance: Make build query condition more robust.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-03-16 13:12:42 +01:00
Dominik Klein
d530e2fdcf Maintenance: Fixed small typo in AI agent system prompt. 2026-03-14 10:44:50 +01:00
Dominik Klein
646ee56b69 Fixes #6010 - Group dispatcher AI Agent doesn't Include category descriptions... 2026-03-14 10:24:34 +01:00
Florian Liebe
7910ed7d22 Fixes #6004 - Users without email are represented by lines when creating a new email.
Co-authored-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-03-13 13:46:47 +01:00
Dominik Klein
6e4cf99565 Fixes #6002 - Knowledge Base shown in sidebar when deactivated in admin panel. 2026-03-12 18:37:36 +01:00
Rolf Schmidt
c1a1c897a4 Maintenance: Ensure that the recent closes table exists to prevent crashes in migrations.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-03-12 11:43:07 +01:00
Dusan Vuckovic
67118383b4 Fixes #5998 - Add a toggle to the AI provider. 2026-03-12 10:21:09 +00:00
Rolf Schmidt
06e846c184 Fixes #5999 - Missing check for User causes error in recent_closes table when DataPrivacyJob is running.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-03-12 10:38:31 +01:00
Martin Gruner
4e6d3d7822 Maintenance: Switch docker image tags to a sortable format
Before, tags hat variable lengths (7.0.0, 7.0.0-9, 7.0.0-12) which prevented alphabetical sorting of the versions (required by tools like updatecli).
This is changed by introducing a fixed format with 4 digits for the commit sequence number (7.0.0-0000, 7.0.0-0012, …).


(cherry picked from commit f766d902fd)

84532bd5 Maintenance: Switch docker image tags to a sortable format
e7909902 Add branch name for CI
6d3264c0 Revert test changes
8579e341 Expand comment

Co-authored-by: Martin Gruner <mg@zammad.com>
2026-03-12 08:34:37 +01:00
Rene Reimann
113beb30ae Fixes #5968 - OTRS import HTTP timed out 2026-03-11 15:25:28 +01:00
Dominik Klein
9143bd74ad Maintenance: Improve AI provider model default values and tests. 2026-03-11 15:09:20 +01:00
Dominik Klein
3c95b7da4e Fixes #5993 - Customer with shared organization has some unexpected fields... 2026-03-10 14:16:16 +01:00
Dusan Vuckovic
71cc9d5f0f Fixes #5983 - Switch knob color in dark mode makes it invisible. 2026-03-09 14:16:37 +00:00
Dusan Vuckovic
680557e836 Maintenance: Stabilize ticket create user test. 2026-03-09 10:51:00 +00:00
Florian Liebe
be60725a69 Fixes #5992 - Mail content is attached as document.html for a non-multipart text/html mail. 2026-03-09 11:16:27 +01:00
Weblate
45b265b015 Maintenance: Update translated files and catalog 2026-03-09 08:34:30 +00:00
Weblate
70789f0ec9 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Anael Mobilia <contrib@anael.eu>
Co-authored-by: NightFlexer <b111vitalii@gmail.com>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Random <random_r@users.noreply.translations.zammad.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: chrand818 <can@telenabler.com>
Co-authored-by: rok-poje <rok.poje@gmail.com>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/fr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/it/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/ru/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sv/
Translation: Zammad/Zammad (pre-release version)
Translation: Zammad/Zammad (stable version)
2026-03-09 08:31:09 +00:00
Martin Gruner
ab53d4894e Fixes #5990 - Railsserver not starting if ZAMMAD_WEB_CONCURRENCY is set to an empty value
(cherry picked from commit 1effa239d6)

891d396d Fixes #5990 - Railsserver not starting if ZAMMAD_WEB_CONCURRENCY is set to an empty value
4b12eb61 Refactor without Rails extensions

Co-authored-by: Martin Gruner <mg@zammad.com>
2026-03-06 21:03:00 +01:00
Dominik Klein
c4c2445148 Maintenance: Desktop view - Improve the situation for not existing options in... 2026-03-06 19:12:49 +01:00
Dusan Vuckovic
86e2533e7b Maintenance: Ensure browser fingerprint stability across different Node.js versions. 2026-03-06 17:25:12 +00:00
Dominik Klein
5c6cef2064 Fixes #5985 - Missing info message about JavaScript is disabled 2026-03-06 17:04:52 +01:00
Mantas Masalskis
6a1d6690ab Fixes #5982 - Upgrading from 6 to 7: redis url not recognized 2026-03-06 12:55:32 +02:00
Marcel Bialas
13e12b474c Fixes #5984 - Fix empty response issue for Ollama reasoning models
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Dominik Klein <dk@zammad.com>
2026-03-05 17:00:15 +01:00
Rolf Schmidt
ab9d33954e Follow up f88d427904 - Fixes #5667 - Add migration for reserved word data.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-03-05 14:49:48 +01:00
Dominik Klein
5fa48c14e2 Fixes #5981 - AI: Custom OpenAI provider fails with Google Gemini due to unsupported "store" parameter. 2026-03-04 19:13:57 +01:00
Rolf Schmidt
bc96ad9b3a Revert "Fixes #5918 - Inline images broken when using article variables."
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-03-04 12:47:38 +01:00
Martin Gruner
f3228274c4 Release Zammad 7.0.0. 🚀 2026-03-04 06:01:32 +01:00
Dusan Vuckovic
0bfa74834a Maintenance: Devcontainer - Add variant with a mailserver. 2026-03-03 15:45:52 +00:00
Zammad Renovate Bot
65a5bb918b Maintenance: Update Vue packages to ^3.5.29 2026-03-03 13:27:31 +00:00
renovatebot
017c272df3 Maintenance: Update dependency oxlint to ^1.50.0 2026-03-03 14:24:59 +01:00
Dusan Vuckovic
d588f05fd5 Fixes #5976 - Incorrect formatting of < and > in text modules. 2026-03-03 12:40:57 +00:00
renovatebot
df804e73d5 Maintenance: Update Vue packages to ^3.5.29 2026-03-03 09:42:58 +01:00
Weblate
c1d4349bb7 Maintenance: Update translated files and catalog 2026-03-03 06:43:17 +00:00
Weblate
bb071d92ad Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Anael Mobilia <contrib@anael.eu>
Co-authored-by: Andi Chandler <andi@gowling.com>
Co-authored-by: Benjamin Scharf <bs@zammad.com>
Co-authored-by: Ivan Perovic <ip@zammad.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/en_GB/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/es/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/fr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hr/
Translation: Zammad/Zammad (pre-release version)
2026-03-02 22:00:38 +00:00
Benjamin Scharf
db3e381e34 Maintenance: Desktop view - Secondary organizations and members lists caches... 2026-03-02 14:20:07 +00:00
Dominik Klein
df2ce8e456 Fixes #5973 - Forgot password on login screen not translated in mobile view. 2026-03-02 12:23:47 +01:00
Dominik Klein
cddddeb79e Fixes #5970 - Ticket Categorizer ignores overrules disabled selection field items 2026-03-02 12:23:25 +01:00
Dominik Klein
e3ab7eda0a Fixes #5974 - Any user with (also) the "customer" role can only select "new" or "closed" in the ticket creation mask 2026-03-02 11:15:23 +01:00
Klaus Mueller
d43ff878d9 Maintenance: Update publiccode.yml to add used by information. 2026-03-02 09:18:50 +01:00
Zammad Weblate
ceae43f736 Translations update from Zammad-Translations 2026-03-02 08:59:14 +01:00
Weblate
130211021f Maintenance: Update translated files and catalog 2026-02-27 12:43:14 +00:00
Weblate
df3660840b Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Balázs Úr <balazs@urbalazs.hu>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Ivan Perovic <ip@zammad.com>
Co-authored-by: Mantas Masalskis <mantas@idev.lt>
Co-authored-by: Murat Duman <mduman@metu.edu.tr>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Random <random_r@users.noreply.translations.zammad.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hu/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/it/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/lt/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/tr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/uk/
Translation: Zammad/Zammad (pre-release version)
2026-02-27 12:39:38 +00:00
Dusan Vuckovic
3aab532cfd Revert "Feature: Editor - Improve signature related content handling" 2026-02-26 16:49:33 +00:00
Balázs Úr
d0abfc34c0 Closes #5952 - Maintenance: Fix typos. 2026-02-26 10:41:01 +00:00
Dusan Vuckovic
fd3fa7a568 Follow-up c88b481601 - Maintenance: Improve translation for AI agents. 2026-02-26 09:35:12 +00:00
Florian Liebe
c88b481601 Maintenance: Improve translation for AI agents. 2026-02-25 14:32:29 +01:00
Mantas Masalskis
597f2b939a Fixes #5927 - Sorting does not work with bigint columns on PostgreSQL 13 2026-02-25 13:04:59 +02:00
Jonas Thelemann
75446f5acf Closes #5961 - Maintenance: Double quote to prevent globbing and word splitting 2026-02-25 10:32:06 +00:00
Mantas Masalskis
cc3f957068 Maintenance: Desktop view - Agent-customers can save agent fields on customer tickets 2026-02-24 18:06:05 +02:00
Marcel Herrguth
7f3c5b1c74 Maintenance: Ensure devcontainer ports are opened locally only 2026-02-24 14:55:59 +00:00
Florian Liebe
9531d33e8c Maintenance: Add missing translate flag for ticket summary configuration page. 2026-02-24 15:39:07 +01:00
Dominik Klein
8450a87113 Maintenance: Desktop view - Improve select handling after ticket subscription updates coming in. 2026-02-24 15:38:56 +01:00
Dusan Vuckovic
16244b0d99 Maintenance: Update Catalan locale breaking change text. 2026-02-24 09:15:25 +00:00
Florian Liebe
0b1d7f8850 Maintenance: Update various gems. 2026-02-24 09:29:47 +01:00
Dusan Vuckovic
cd10579929 Maintenance: Improve field checkbox styles. 2026-02-23 16:16:49 +00:00
Weblate
f0c09af711 Maintenance: Update translated files and catalog 2026-02-23 11:31:02 +00:00
Weblate
77f0f33b05 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Anael Mobilia <contrib@anael.eu>
Co-authored-by: Andi Chandler <andi@gowling.com>
Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Ivan Perovic <ip@zammad.com>
Co-authored-by: Mantas Masalskis <mantas@idev.lt>
Co-authored-by: Murat Duman <mduman@metu.edu.tr>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Random <random_r@users.noreply.translations.zammad.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: iLocIT! <matthiasc@ilocit.de>
Co-authored-by: mathew-uems <mathew.chen@edgentauems.com.tw>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/en_GB/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/fr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/it/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/lt/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/tr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/uk/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/zh_Hant/
Translation: Zammad/Zammad (pre-release version)
2026-02-23 11:26:52 +00:00
Martin Gruner
3527c24e91 Maintenance: Add missing entries to breaking changes for 7.0 2026-02-23 11:13:04 +01:00
Rolf Schmidt
e53d64267d Maintenance: Improve error message for disabled graphql introspection command.
Co-authored-by: Florian Liebe <fl@zammad.com>


(cherry picked from commit 74d71595e4)

501597b0 Maintenance: Improve error message for disabled graphql introspection command.
cb4c6d44 improve check

Co-authored-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-02-23 11:12:16 +01:00
Rolf Schmidt
2043f46496 Maintenance: Make zammad:package:precompile command working again.
(cherry picked from commit 7cd037775c)

7cd03777 Maintenance: Make zammad:package:precompile command working again.

Co-authored-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-02-23 10:23:50 +01:00
Mantas Masalskis
49457623e3 Maintenance: Clarify Macro administrator permission 2026-02-21 20:01:26 +02:00
Florian Liebe
7d6dead38f Maintenance: Improve handling of internal object manager attributes in desktop view. 2026-02-20 21:51:12 +01:00
Dusan Vuckovic
61a37f1d7a Maintenance: Desktop view - Improve handling of dirty fields in edit user form. 2026-02-20 15:44:58 +00:00
Benjamin Scharf
5e3f78fbbb Maintenance: Desktop view - Editor - Allow decrease of indentation level in lists 2026-02-20 16:40:55 +01:00
Dusan Vuckovic
006c5df722 Maintenance: Consolidate import names of Vue components. 2026-02-20 14:35:41 +00:00
Ben
67995d28fc Maintenance: Desktop view - Fix locale for ticket create 2026-02-20 15:32:08 +01:00
Benjamin Scharf
8e0993ce98 Feature: Desktop view - Prefetch editor component 2026-02-20 15:31:43 +01:00
Florian Liebe
352be82348 Fixes #5959 - Ruby exception raised in the log on some M365 errors. 2026-02-20 15:21:34 +01:00
Dusan Vuckovic
6e657827e5 Maintenance: Desktop view - Validation errors may not be applied to object attributes with kebab_case names.
(cherry picked from commit f7dc793434)

b89e9383 Maintenance: Desktop view - Validation errors may not be applied to object...
16ed4def test: Tweak spec.

Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-02-20 14:15:34 +00:00
Dusan Vuckovic
8cdd4e365c Maintenance: Desktop view - Ticket Detail View: Taskbar is not closed after merging.
(cherry picked from commit 239ef05f07)

239ef05f Maintenance: Desktop view - Ticket Detail View: Taskbar is not closed after merging.

Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-02-20 13:49:52 +00:00
Dusan Vuckovic
d71c2cb037 Maintenance: Desktop view - The password field should be cleared on login errors.
(cherry picked from commit bb75a7b72a)

773b173c Mainteance: Desktop view - The password field should be cleared on login errors.
d30a3045 test: Extend specs.
a41d0cdc fix: Pass current login to password reset screen in desktop view.

Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-02-20 13:09:08 +00:00
Dusan Vuckovic
7eab068347 Maintenance: Desktop view - Add missing diff, future and past data option handling to date and datetime fields.
(cherry picked from commit 909c00f470)

909c00f4 Maintenance: Desktop view - Add missing diff, future and past data option...

Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-02-20 13:08:51 +00:00
Mantas Masalskis
2f629ed7b1 Maintenance: Ticket Detail View - Some ticket sidebars have a wrong permission... 2026-02-20 14:28:32 +02:00
Florian Liebe
0110b3f429 Maintenance: Desktop View - Improve handling of ticket split to allow customization of title + body. 2026-02-20 12:08:11 +01:00
Florian Liebe
839c97b5fe Fixes #5958 - Object attributes can be created with an empty display label.
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-02-20 11:51:09 +01:00
Florian Liebe
6a4281287a Maintenance: Improve organization detail view history spec.
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-02-20 11:04:20 +01:00
Florian Liebe
b0368f6811 Maintenance: Update Vue packages to ^3.5.28 2026-02-20 10:31:33 +01:00
Benjamin Scharf
f44931d8a0 Maintenance: Desktop view - ListRecentCloses access forbidden error for admin-only users 2026-02-19 21:18:13 +01:00
Dusan Vuckovic
da85f46ca8 Maintenance: Cleanup stray fills from icon assets. 2026-02-19 14:24:17 +00:00
Dusan Vuckovic
8284364d66 Maintenance: Desktop view - Fix display of previewable image types in ticket attachments. 2026-02-19 14:22:56 +00:00
Martin Gruner
51b5289f53 Maintenance: Fix POSTGRESQL_OPTIONS handling when constructing DATABASE_URL 2026-02-19 14:42:44 +01:00
Florian Liebe
eebdfec5ee Maintenance: Improve handling of 'report' permission in ticket policy. 2026-02-19 11:20:17 +01:00
Florian Liebe
4629dfade5 Maintenance: Desktop View - Improve grouping feature for overviews if custom object manager attributes are used. 2026-02-19 11:19:57 +01:00
Mantas Masalskis
172fb27655 Maintenance: Ticket Detail View - Too many fields visible when agent+customer... 2026-02-19 11:56:45 +02:00
Dominik Klein
1e5068c795 Maintenance: Desktop view - Improve organization field handling in the ticket create form. 2026-02-19 10:51:58 +01:00
Dusan Vuckovic
cae36fccbc Fixes #5948 - Default time diff is applying wrong units when the associated fields are missing. 2026-02-18 18:13:43 +00:00
Dusan Vuckovic
f5120d6dd8 Maintenance: Desktop view - Empty searches and widgets should be hidden. 2026-02-18 12:34:01 +00:00
Dusan Vuckovic
7885467e17 Maintenance: Desktop view - Remove keyboard shortcut item from user menu. 2026-02-18 12:26:55 +00:00
Weblate
467a78a1a6 Maintenance: Update translated files and catalog 2026-02-18 06:35:53 +00:00
Weblate
0ecd35551e Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Andre <8a9315x8s@mozmail.com>
Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Balázs Úr <balazs@urbalazs.hu>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Ivan Perovic <ip@zammad.com>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/hu/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/pt_PT/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/uk/
Translation: Zammad/Zammad (pre-release version)
2026-02-17 22:09:44 +00:00
Rolf Schmidt
c625a55a81 Maintenance: Fix naming for block operators.
Co-authored-by: Dominik Klein <dk@zammad.com>
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-02-17 17:25:51 +01:00
Dusan Vuckovic
27525e1d1d Fixes #5949 - CSRF token verification failed. 2026-02-17 14:48:31 +00:00
Ralf Schmid
31d2be40f6 Maintenance: Adjust links to new documentation 2026-02-16 11:45:43 +01:00
Weblate
9428c0a32a Maintenance: Update translated files and catalog 2026-02-16 06:15:56 +00:00
Weblate
f9eb947699 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Andi Chandler <andi@gowling.com>
Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Dennis Juhler Aagaard <dennis@kulmosen.dk>
Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Random <random_r@users.noreply.translations.zammad.org>
Co-authored-by: Tecsi Aron <aron@edituraedu.ro>
Co-authored-by: Victor Gallegos <vgallegos@kaliroy.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: chrand818 <can@telenabler.com>
Co-authored-by: easyjoh <jh@easyplusplus.com>
Co-authored-by: mathew-uems <mathew.chen@edgentauems.com.tw>
Co-authored-by: Øivind Spro Heggland <oivheg@gmail.com>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/da/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/en_GB/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/es_MX/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/it/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/nb_NO/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/ro/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/sv/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/uk/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-stable/zh_Hant/
Translation: Zammad/Zammad (pre-release version)
2026-02-15 22:00:37 +00:00
Florian Liebe
8579ce7a1b Maintenance: Fix linting issues. 2026-02-15 13:16:08 +01:00
Marcel Bialas
6e4daffb0a Maintenance: Desktop View - Improve text truncation behavior across new UI 2026-02-13 16:22:13 +01:00
Marcel Bialas
2810e507e2 Maintenance: Desktop View - Improve truncation for long overview names 2026-02-13 14:42:09 +01:00
Benjamin Scharf
4ef08f051c Maintenance: Desktop view - Stabilise frontend spec 2026-02-13 13:12:04 +01:00
Florian Liebe
55b64ccf2d Fixes #5946 - Custom ticket attribute changes owner name in ticket history. 2026-02-13 12:55:55 +01:00
Martin Gruner
d4ca6c1947 Maintenance: drop obsolete docker compose version attribute
(cherry picked from commit c44c225b61)

d198b2a2 Maintenance: drop obsolete docker compose version attribute
e525f1c4 Revert curl call, add debug.
11f8e360 Undo debug line

Co-authored-by: Martin Gruner <mg@zammad.com>
2026-02-13 12:09:23 +01:00
Dusan Vuckovic
f1604c16e1 Maintenance: Reconfigure TailwindCSS plugin for prettier. 2026-02-13 08:23:25 +00:00
Mantas Masalskis
ff5f127fcd Maintenance: Ensure new migrations use integer primary keys consistently 2026-02-12 17:32:22 +02:00
Dusan Vuckovic
f2b5556fa1 Fixes #5947 - AI Assistant Writing Tools: paragraph differences in dialog and editor. 2026-02-12 11:47:35 +00:00
Florian Liebe
95b2fd48ec Maintenance: Update gem dependencies. 2026-02-12 10:29:19 +01:00
Mantas Masalskis
6a699a0096 Maintenance: Adds Catalan locale changes to Breaking Changes file 2026-02-11 13:33:22 +02:00
Ben
5ba5dc26d8 Maintenance: Desktop view - Dynamically import date-fns locales to reduce bundle size 2026-02-11 08:58:14 +01:00
Dominik Klein
aecebc6b10 Maintenance: Desktop view - Improve overlay container usage for multiple usages on the same page. 2026-02-10 17:15:46 +01:00
Mantas Masalskis
f7ee8d7e9c Fixes #5945 - migrate user preferences to new Catalan locale code 2026-02-10 17:34:27 +02:00
Dominik Klein
89c2f8f238 Maintenance: Desktop view - Improve history fragment cache handling to avoid loading of introspection file. 2026-02-10 11:40:27 +01:00
Florian Liebe
d86ad289d0 Update release date. 2026-02-10 08:11:45 +01:00
Martin Gruner
0776909ad6 Maintenance: Update version specific files for 7.0 2026-02-09 19:07:03 +01:00
Florian Liebe
1767a194c9 Maintenance: Improve ticket_related endpoint.
Co-authored-by: Dominik Klein <dk@zammad.com>
2026-02-09 18:59:58 +01:00
Florian Liebe
01ecdd5cdf Maintenance: Improve import related endpoints.
Co-authored-by: Dominik Klein <dk@zammad.com>
2026-02-09 18:59:58 +01:00
Florian Liebe
15937a00d1 Maintenance: Improve ticket update handling using REST API/GraphQL mutation.
Co-authored-by: Dominik Klein <dk@zammad.com>
2026-02-09 18:59:58 +01:00
Mantas Masalskis
1ad526ef41 Maintenance: Improve configuration details returned by the server.
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Florian Liebe <fl@zammad.com>
Co-authored-by: Mantas Masalskis <mm@zammad.com>
Co-authored-by: Marcel Bialas <mb@zammad.com>
Co-authored-by: Martin Gruner <mg@zammad.com>
2026-02-09 18:59:58 +01:00
1074 changed files with 102529 additions and 79451 deletions

View File

@@ -1,2 +1,2 @@
https://github.com/heroku/heroku-buildpack-nodejs.git#v280
https://github.com/heroku/heroku-buildpack-nodejs.git#v340
https://github.com/pkgr/heroku-buildpack-ruby.git#v267-1

10
.coderabbit.yml Normal file
View File

@@ -0,0 +1,10 @@
inheritance: true
reviews:
review_status: false
auto_review:
enabled: true
base_branches:
- 'develop'
ignore_usernames:
- 'renovatebot'
drafts: false

View File

@@ -23,7 +23,7 @@ Purpose: Give an AI coding agent a fast, reliable mental model of this repo to s
- Legacy desktop-app: CoffeeScript, Spine.js, Sprockets, REST API
(see [`../app/assets/`](../app/assets/), [`../coffeelint.json`](../coffeelint.json), QUnit in `../test/`)
- New desktop-view and mobile apps: Vue 3, TypeScript, Pinia, Apollo Client (GraphQL), Tailwind CSS, VueUse,
Vitest, Testing Library, Cypress, pnpm, vite-plugin-ruby, vite-plugin-pwa, ESLint, Stylelint, Prettier
Vitest, Testing Library, Cypress, pnpm, vite-plugin-ruby, vite-plugin-pwa, ESLint, Stylelint, Oxfmt
(see [`../package.json`](../package.json), [`../vite.config.mjs`](../vite.config.mjs),
[`../tsconfig.base.json`](../tsconfig.base.json), [`../eslint.config.ts`](../eslint.config.ts))
- Backend: Ruby on Rails, PostgreSQL, Redis, ActionCable, Delayed Job, GraphQL
@@ -86,7 +86,7 @@ Purpose: Give an AI coding agent a fast, reliable mental model of this repo to s
- Use path aliases from [`../tsconfig.base.json`](../tsconfig.base.json).
- Do not cross-import between desktop/mobile apps (ESLint enforces boundaries).
- Use Vitest and Testing Library for unit/component tests ([`../app/frontend/tests/`](../app/frontend/tests/)).
- Use Tailwind CSS utilities for styling. Lint with Stylelint and Prettier.
- Use Tailwind CSS utilities for styling. Lint with Stylelint and Oxfmt.
- For i18n, wrap user-facing strings and see [`../eslint.config.ts`](../eslint.config.ts) for rules.
## When in doubt

View File

@@ -1,5 +1,4 @@
version: '3.6'
---
volumes:
visual-regression-dependencies-amd64:

View File

@@ -1,5 +1,4 @@
version: '3.6'
---
volumes:
visual-regression-dependencies-arm:

View File

@@ -0,0 +1,49 @@
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
module RuboCop
module Cop
module Zammad
module CanCheck51PlusMigrations
VERSION_THRESHOLD = 5.1
# Apply to 7.0 migrations and newer
MIGRATION_THRESHOLD = 20250519095403 # rubocop:disable Style/NumericLiterals
def migration_51_or_newer?(node)
return false if old_migration?
migration?(node) && rails_51_or_newer?(node)
end
private
def migration?(node)
superclass(node)&.children&.at(0)&.const_name == 'ActiveRecord::Migration'
end
def old_migration?
file_timestamp = File.basename(processed_source.file_path)[%r{\A\d+}]
return false if !file_timestamp
file_timestamp.to_i < MIGRATION_THRESHOLD
end
def rails_51_or_newer?(node)
klass = superclass(node)
return false if !klass.send_type?
klass.arguments.first.value.to_f >= VERSION_THRESHOLD
end
def superclass(node)
node
.each_ancestor(:class)
.first
&.parent_class
end
end
end
end
end

View File

@@ -0,0 +1,53 @@
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
# frozen_string_literal: true
require_relative 'concerns/can_check_51_plus_migrations'
module RuboCop
module Cop
module Zammad
class MigrationEnsureIntegerForeignKey < Base
extend AutoCorrector
include CanCheck51PlusMigrations
MSG = 'Rails 5.1+ migrations must add type: :integer to references setup.'
RESTRICT_ON_SEND = %i[
references
belongs_to
add_reference
add_references
].freeze
def on_send(node)
return if !migration_51_or_newer?(node)
options = node.arguments.last
pair_with_type = nil
if options&.hash_type?
options.pairs.each do |pair|
next if !(pair.key.sym_type? && pair.key.value == :type)
pair_with_type = pair
next if pair.value.source == ':integer'
next if pair.value.source == ':bigint' # allow explicit bigint foreign keys
add_offense(pair.value) do |corrector|
corrector.replace(pair.value, ':integer')
end
end
end
return if pair_with_type
add_offense(node) do |corrector|
corrector.insert_after(node.arguments.last.source_range, ', type: :integer')
end
end
end
end
end
end

View File

@@ -0,0 +1,49 @@
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
# frozen_string_literal: true
require_relative 'concerns/can_check_51_plus_migrations'
module RuboCop
module Cop
module Zammad
class MigrationEnsureIntegerPrimaryKey < Base
extend AutoCorrector
include CanCheck51PlusMigrations
MSG = 'Rails 5.1+ migrations must add id: :integer to ensure primary key is integer, not bigint.'
RESTRICT_ON_SEND = %i[create_table].freeze
def on_send(node)
return if !migration_51_or_newer?(node)
options = node.arguments.last
pair_with_id = nil
if options&.hash_type?
options.pairs.each do |pair|
next if !(pair.key.sym_type? && pair.key.value == :id)
pair_with_id = pair
next if pair.value.source == ':integer'
next if pair.value.source == 'false' # allow id: false for ID-less tables
next if pair.value.source == ':bigserial' # allow id: :bigserial for explicit bigint primary keys
add_offense(pair.value) do |corrector|
corrector.replace(pair.value, ':integer')
end
end
end
return if pair_with_id
add_offense(node) do |corrector|
corrector.insert_after(node.arguments.first.source_range, ', id: :integer')
end
end
end
end
end
end

View File

@@ -531,6 +531,16 @@ Zammad/GraphqlCsrfSkipVerification:
Include:
- "app/graphql/gql/**/*.rb"
Zammad/MigrationEnsureIntegerPrimaryKey:
Enabled: true
Include:
- "db/migrate/*.rb"
Zammad/MigrationEnsureIntegerForeignKey:
Enabled: true
Include:
- "db/migrate/*.rb"
Capybara/NegationMatcherAfterVisit:
Description: 'Allow to use negated matcher after visit'
Enabled: false

View File

@@ -30,3 +30,5 @@ require_relative 'cop/zammad/to_forbid_over_not_to_permit'
require_relative 'cop/zammad/trigger_from_commit_hooks'
require_relative 'cop/zammad/update_copyright'
require_relative 'cop/zammad/ensures_time_zone_at_spec_top_level'
require_relative 'cop/zammad/migration_ensure_integer_primary_key'
require_relative 'cop/zammad/migration_ensure_integer_foreign_key'

View File

@@ -50,7 +50,6 @@ Metrics/AbcSize:
- 'app/models/avatar.rb'
- 'app/models/calendar.rb'
- 'app/models/channel.rb'
- 'app/models/channel/assets.rb'
- 'app/models/channel/email_build.rb'
- 'app/models/channel/email_parser.rb'
- 'app/models/channel/filter/auto_response_check.rb'
@@ -260,7 +259,6 @@ Metrics/CyclomaticComplexity:
- 'app/models/avatar.rb'
- 'app/models/calendar.rb'
- 'app/models/channel.rb'
- 'app/models/channel/assets.rb'
- 'app/models/channel/email_build.rb'
- 'app/models/channel/email_parser.rb'
- 'app/models/channel/filter/auto_response_check.rb'
@@ -383,7 +381,6 @@ Metrics/PerceivedComplexity:
- 'app/models/avatar.rb'
- 'app/models/calendar.rb'
- 'app/models/channel.rb'
- 'app/models/channel/assets.rb'
- 'app/models/channel/email_build.rb'
- 'app/models/channel/email_parser.rb'
- 'app/models/channel/filter/auto_response_check.rb'

View File

@@ -42,7 +42,7 @@ services:
restart: unless-stopped
ports:
# Expose the ES service for direct access if needed, but avoid conflicts with host services.
- 19200:9200
- 127.0.0.1:19200:9200
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
@@ -69,4 +69,4 @@ volumes:
elasticsearch-data:
driver: local
postgresql-data:
driver: local
driver: local

View File

@@ -31,7 +31,6 @@
"dbaeumer.vscode-eslint",
"dotjoshjohnson.xml",
"eamodio.gitlens",
"esbenp.prettier-vscode",
"ezforo.copy-relative-path-and-line-numbers",
"jock.svg",
"kaiwood.endwise",

View File

@@ -0,0 +1,83 @@
{
"Users": [
{
"login": "admin@example.com",
"firstname": "Test Admin",
"lastname": "Agent",
"email": "admin@example.com",
"password": "test"
},
{
"login": "agent1@example.com",
"firstname": "Agent 1",
"lastname": "Test",
"email": "agent1@example.com",
"password": "test",
"roles": ["Agent"]
}
],
"Groups": [
{
"name": "some group1",
"users": ["admin@example.com", "agent1@example.com"]
},
{
"name": "Users",
"users": ["admin@example.com", "agent1@example.com"],
"signature": "default",
"email_address_id": 1
}
],
"Channels": [
{
"id": 3,
"area": "Email::Account",
"group": "Users",
"active": true,
"options": {
"inbound": {
"adapter": "imap",
"options": {
"host": "mail.test.local",
"port": "993",
"ssl": "ssl",
"user": "zammad@test.local",
"password": "zammad",
"ssl_verify": true
}
},
"outbound": {
"adapter": "smtp",
"options": {
"host": "mail.test.local",
"port": "465",
"user": "zammad@test.local",
"password": "zammad",
"ssl_verify": true
}
}
}
}
],
"EmailAddresses": [
{
"id": 1,
"channel_id": 3,
"name": "Zammad Helpdesk",
"email": "zammad@test.local"
}
],
"Settings": [
{
"name": "product_name",
"value": "Zammad Test System"
},
{
"name": "developer_mode",
"value": true
}
],
"TextModuleLocale": {
"Locale": "de-de"
}
}

View File

@@ -0,0 +1,46 @@
name: "zammad-devcontainer"
include: [../default/compose.yaml]
services:
mailserver-init:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
entrypoint: ["/bin/sh", "-c", "sh /tmp/docker-mailserver/scripts/init-mailserver.sh"]
environment:
ACCOUNT_PROVISIONER: FILE
volumes:
- ./scripts:/tmp/docker-mailserver/scripts:ro
- ./certs:/tmp/docker-mailserver/certs
- mailserver-config:/tmp/docker-mailserver
restart: no
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
hostname: mail.test.local
domainname: test.local
environment:
SSL_TYPE: manual
SSL_CERT_PATH: /tmp/docker-mailserver/certs/mail.test.local.crt
SSL_KEY_PATH: /tmp/docker-mailserver/certs/mail.test.local.key
ENABLE_IMAP: "1"
ENABLE_POP3: "1"
DMS_DEBUG: "0"
ONE_DIR: "1"
POSTMASTER_ADDRESS: postmaster@test.local
OVERRIDE_HOSTNAME: mail.test.local
ACCOUNT_PROVISIONER: FILE
depends_on:
mailserver-init:
condition: service_completed_successfully
ports:
- 127.0.0.1:1465:465 # SMTP (SSL)
- 127.0.0.1:1587:587 # SMTP (STARTTLS)
- 127.0.0.1:1993:993 # IMAP (SSL)
volumes:
- ./certs:/tmp/docker-mailserver/certs
- mailserver-config:/tmp/docker-mailserver
restart: unless-stopped
volumes:
mailserver-config:
driver: local

View File

@@ -0,0 +1,30 @@
// For format details, see https://containers.dev/implementors/json_reference/.
// For config options, see the README at: https://github.com/devcontainers/templates/tree/main/src/ruby
{
"name": "Zammad (with Mailserver)",
"dockerComposeFile": "compose.yaml",
"service": "devcontainer",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"features": {
"../features/zammad-deps": {},
"../features/add-bin-path": {
"addBinPath": "/workspaces/${localWorkspaceFolderBasename}/bin"
}
},
"mounts": [
{
// Avoid conflicts with host-installed node_modules with a different platform/architecture.
"source": "node-modules",
"target": "/workspaces/${localWorkspaceFolderBasename}/node_modules",
"type": "volume"
}
],
"containerEnv": {
// ZAMMAD_MAIL_TO_FILE is for debugging outgoing mails.
// It allows to easily inspect contents of the outgoing messages that use `sendmail` adapter,
// by writing them to files instead of sending them. Check the `tmp/mails` directory for the generated files.
"ZAMMAD_MAIL_TO_FILE": "1"
},
"forwardPorts": [3000, 3001, 3036, 6042],
"postCreateCommand": "/workspaces/${localWorkspaceFolderBasename}/.devcontainer/with-mailserver/scripts/post_create.sh"
}

View File

@@ -0,0 +1,42 @@
#!/bin/sh
set -eu
CERT_DIR=/tmp/docker-mailserver/certs
CONFIG_DIR=/tmp/docker-mailserver
CERT_FILE="$CERT_DIR/mail.test.local.crt"
KEY_FILE="$CERT_DIR/mail.test.local.key"
POSTFIX_ACCOUNTS_FILE="$CONFIG_DIR/postfix-accounts.cf"
POSTFIX_VIRTUAL_FILE="$CONFIG_DIR/postfix-virtual.cf"
POSTFIX_MAIN_FILE="$CONFIG_DIR/postfix-main.cf"
mkdir -p "$CERT_DIR"
mkdir -p "$CONFIG_DIR"
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
-keyout "$KEY_FILE" \
-out "$CERT_FILE" \
-subj "/CN=mail.test.local" \
-addext "subjectAltName=DNS:mail.test.local,DNS:localhost"
fi
if [ ! -f "$POSTFIX_ACCOUNTS_FILE" ]; then
touch "$POSTFIX_ACCOUNTS_FILE"
setup email add zammad@test.local zammad
elif ! setup email list | grep -q 'zammad@test.local$'; then
setup email add zammad@test.local zammad
fi
if [ ! -f "$POSTFIX_VIRTUAL_FILE" ]; then
touch "$POSTFIX_VIRTUAL_FILE"
setup alias add zammad@localhost zammad@test.local
elif ! setup alias list | grep -q 'zammad@localhost'; then
setup alias add zammad@localhost zammad@test.local
fi
cat > "$POSTFIX_MAIN_FILE" <<'EOF'
smtpd_recipient_restrictions = permit_sasl_authenticated,reject
relay_transport = discard:
default_transport = discard:
EOF

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env sh
env
# Ensure proper permissions on root-mounted volume.
sudo chown -R "${USER}" node_modules
bin/setup --skip-server
bundle exec bootsnap precompile --gemfile app/ lib/
bundle exec rails r "Setting.set('es_url', 'http://elasticsearch:9200')"
bundle exec rails zammad:setup:auto_wizard[.devcontainer/with-mailserver/auto_wizard_mailserver.json]
CERT_PATH=.devcontainer/with-mailserver/certs/mail.test.local.crt
# Import self-signed certificate for mailserver.
if [ -f "$CERT_PATH" ]; then
bundle exec rails r "SSLCertificate.create!(certificate: Rails.root.join('$CERT_PATH').read)"
fi

View File

@@ -19,7 +19,7 @@ services:
ollama:
image: docker.io/ollama/ollama:latest
ports:
- 11434:11434
- 127.0.0.1:11434:11434
volumes:
- ollama-data:/root/.ollama
restart: unless-stopped

View File

@@ -11,7 +11,7 @@ services:
SE_NODE_OVERRIDE_MAX_SESSIONS: 'true'
SE_VNC_NO_PASSWORD: 'true'
ports:
- 4444:4444
- 127.0.0.1:4444:4444
restart: unless-stopped
selenium-firefox:
@@ -23,5 +23,5 @@ services:
SE_OPTS: '--port 4445'
SE_VNC_NO_PASSWORD: 'true'
ports:
- 4445:4445
- 127.0.0.1:4445:4445
restart: unless-stopped

View File

@@ -23,7 +23,7 @@ const year = new Date().getFullYear()
const ruleTester = new RuleTester({
languageOptions: {
parser: require('vue-eslint-parser'), // required to parse .vue files properly otherwise js-parser will be used
}
},
})
ruleTester.run('zammad-copyright', rule, {

View File

@@ -56,8 +56,7 @@ ruleTester.run('zammad-detect-translatable-string', rule, {
code: `'String that should be translatable'`,
errors: [
{
message:
'This string looks like it should be marked as translatable via __(...)',
message: 'This string looks like it should be marked as translatable via __(...)',
},
],
output: `__('String that should be translatable')`,

View File

@@ -1,6 +1,5 @@
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
/**
* @fileoverview Enforce kebab-case for Symbol descriptors
* @author Benjamin Scharf
@@ -21,7 +20,7 @@ const rule = require('../../../lib/rules/zammad-symbol-description.js')
const ruleTester = new RuleTester({
languageOptions: {
parser: require('vue-eslint-parser'), // required to parse .vue files properly otherwise js-parser will be used
}
},
})
ruleTester.run('zammad-symbol-descriptor', rule, {

View File

@@ -17,8 +17,7 @@ const rule = require('../../../lib/rules/zammad-tailwind-ltr.js')
// Tests
//------------------------------------------------------------------------------
const error =
'When positioning classes are used, they must be prefixed with ltr/rtl.'
const error = 'When positioning classes are used, they must be prefixed with ltr/rtl.'
const ruleTester = new RuleTester()
ruleTester.run('zammad-tailwind-ltr', rule, {

View File

@@ -137,7 +137,9 @@ jobs:
type=sha,suffix=-arm64
- name: Get incrementing Zammad version from the most recent tag
run: echo ZAMMAD_VERSION="$(git describe --tags | sed -e 's/-[a-z0-9]\{8,\}.*//g')" >> $GITHUB_ENV
# Make sure the number is always present (via --long), and pad it to four digits
# for proper alphabetical sorting in tools like updatecli (e.g. 7.0.0-0000, 7.1.0-0215).
run: echo ZAMMAD_VERSION="$(git describe --tags --long | awk -F'-' '{printf "%s-%04d\n", $1, $(NF-1)}')" >> $GITHUB_ENV
- name: Get Zammad major.minor version from the most recent tag
run: echo ZAMMAD_VERSION_MAJOR_MINOR="$(git describe --tags | cut -f1-2 -d.)" >> $GITHUB_ENV

View File

@@ -3,11 +3,16 @@ on:
push:
branches:
- stable*
# Test builds for public branches based on develop. Don't forget to clean up test branches in go.packager.io after merging.
- develop-*
schedule:
# Build for develop only once per workday to save space in packagehall.
- cron: '20 6 * * 1-5'
workflow_dispatch:
permissions:
contents: read
jobs:
build:
if: github.repository == 'zammad/zammad'
@@ -34,7 +39,7 @@ jobs:
VERSION=$(cat VERSION)
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Package
uses: pkgr/action/package@main
uses: pkgr/action/package@v1
id: package
with:
target: ${{ matrix.target }}
@@ -45,10 +50,10 @@ jobs:
DATABASE_URL=postgresql://zammad:/zammad
ZAMMAD_SAFE_MODE=1
- name: Publish
uses: pkgr/action/publish@main
uses: pkgr/action/publish@v1
with:
target: ${{ matrix.target }}
token: ${{ secrets.PACKAGER_PUBLISH_TOKEN }}
repository: zammad/zammad
channel: ${{ github.ref_name }}
file: ${{ steps.package.outputs.package_path }}
file: ${{ steps.package.outputs.package_path }}

4
.gitignore vendored
View File

@@ -122,3 +122,7 @@ schema.graphql
# Packages
*.zpm
# Devcontainer
/.devcontainer/with-mailserver/certs/*
!/.devcontainer/with-mailserver/certs/.gitkeep

View File

@@ -103,6 +103,8 @@
- pnpm lint:ts
- echo "ESLint check…"
- pnpm lint:js
- echo "Oxfmt check…"
- pnpm format:check
- pnpm analyse:vite-bundle -t list -o ./tmp/vite-bundle-stats.yml
- .gitlab/verify_vite_bundle_size.rb
# Artifacts are stored for failed jobs

View File

@@ -13,7 +13,6 @@
curl https://raw.githubusercontent.com/zammad/zammad-docker-compose/master/docker-compose.yml > docker-compose.yml
cat - <<YML_FILE > docker-compose.review-app.yml
---
version: '3.8'
services:
zammad-init:
environment:

View File

@@ -2,9 +2,6 @@
set -eu
# Workaround: postgresql fails on rhel-9 when it gets installed together with Zammad.
dnf install -y postgresql-server
dnf install -y zammad
curl --retry 30 --retry-delay 1 --retry-connrefused http://localhost:3000 | grep "Zammad Helpdesk"

View File

@@ -89,7 +89,6 @@
"pinia",
"/^@vue/",
"/^vue/",
"!@vue/eslint-config-prettier",
"!@vue/eslint-config-typescript",
"!@vuepic/vue-datepicker"
],
@@ -97,7 +96,7 @@
"groupSlug": "vue"
},
{
"matchPackageNames": ["@vue/eslint-config-prettier", "@vue/eslint-config-typescript"],
"matchPackageNames": ["@vue/eslint-config-typescript"],
"groupName": "Vue ESLint Configs",
"groupSlug": "vue-eslint-configs"
},

View File

@@ -95,6 +95,17 @@ PreCommit:
include:
- 'app/frontend/**/*'
- '.eslint-plugin-zammad/**/*'
Oxfmt:
required_executable: 'pnpm'
enabled: true
command: ['pnpm', 'format:check:cmd']
flags: ['app/frontend/', '.eslint-plugin-zammad/']
include:
- 'app/frontend/**/*.js'
- 'app/frontend/**/*.ts'
- 'app/frontend/**/*.vue'
- '.eslint-plugin-zammad/**/*.js'
- '.eslint-plugin-zammad/**/*.ts'
Markdownlint:
required_executable: 'pnpm'
enabled: true

30
.oxfmtrc.json Normal file
View File

@@ -0,0 +1,30 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 100,
"sortTailwindcss": {
"stylesheet": "./app/frontend/apps/desktop/styles/main.css"
},
"sortPackageJson": false,
"overrides": [
{
"files": ["./app/frontend/apps/desktop/**"],
"options": {
"sortTailwindcss": { "stylesheet": "./app/frontend/apps/desktop/styles/main.css" }
}
},
{
"files": ["./app/frontend/apps/mobile/**"],
"options": {
"sortTailwindcss": { "stylesheet": "./app/frontend/apps/mobile/styles/main.css" }
}
}
],
"ignorePatterns": [
"app/frontend/**/graphql/**/*.ts",
"!app/frontend/tests/graphql/**/*.ts",
"public/**/*",
"tmp/**/*",
".dev/rubocop/**/*.yml"
]
}

View File

@@ -9,7 +9,6 @@ targets:
- elasticsearch
- nginx
- postgresql-server
- which
- epel-release
- imlib2
- shared-mime-info
@@ -99,7 +98,6 @@ targets:
- psmisc
- procps
- libopenssl1_1
- which
build_dependencies:
# Add packages required for build that are not in the official SLES repo.
# Direct URLs must be used since we cannot add repos on packager.io

View File

@@ -1,7 +0,0 @@
**/graphql/**/*.api.ts
!**/graphql/**/mocks/*.ts
!**/graphql/**/*.spec.ts
!**/tests/graphql/**
public/**/*
tmp/**/*
.dev/rubocop/**/*.yml

View File

@@ -1,7 +0,0 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}

View File

@@ -2,8 +2,7 @@
"extends": [
"stylelint-config-standard",
"stylelint-config-recommended-scss",
"stylelint-config-recommended-vue",
"stylelint-prettier/recommended"
"stylelint-config-recommended-vue"
],
"rules": {
"alpha-value-notation": null,

View File

@@ -55,6 +55,31 @@ state after the update.
User records cannot have the same organization set as both primary and secondary anymore, and trying to
do this will now result in an API error.
### Catalan locale change
The previously available Catalan locale used a wrong internal locale code and was deprecated. There is now a
new Catalan locale with the correct code "ca". The deprecated locale will be removed in a future release of Zammad.
An automatic update will switch the language preference of all Catalan user profiles to the new 'Catalan (Català)'
locale during the migration.
The following applies only in case you are already using Knowledge Base with Catalan language. Please note the
deprecated Catalan locale will not be updated automatically. Changing the locale of a Knowledge Base causes a change in
the public URLs. You can migrate Knowledge Base at your own pace by running the following command:
```ruby
zammad run rails r "KnowledgeBase::Locale.find_by(system_locale: Locale.find_by(locale: 'es-ca'))&.update!(system_locale: Locale.find_by(locale: 'ca'))"
```
### Slack integration removed
The slack integration was remved from the codebase. It is recommended that you
[switch to pre-built webhooks instead](https://admin-docs.zammad.org/en/latest/manage/webhook/examples/slack-notifications.html).
Existing Slack integrations should be migrated manually, if not already done.
### Twitter integration removed
The Twitter integration was removed due to problems with API licensing. There is no replacement available.
## 6.5.2
The following breaking changes occurred due to a security fix.
@@ -101,7 +126,7 @@ The structure of the **full search** (e.g. `/ticket/search?full=true`) remains t
Some objects used an object-related hash key, such as `ticket_ids`. This is now always `record_ids`.
The **count search** (e.g. `/ticket/search?only_total_count=true`) is a new feature.
The **count search** (e.g. `/ticket/search?only_total_count=true`) is a new feature.
### API performance optimization of asset return data
@@ -244,8 +269,8 @@ now it is forbidden.
On existing systems, the group names that contain the now reserved delimiter will be renamed, with sets of double colons
being replaced by a dash (`-`) during the migration process.
Additionally, existing custom group object attributes named _name\_last_ and _parent\_id_ will be renamed too, by adding
an underscore in front (_\_name\_last_ and _\_parent\_id_). This is due to these attributes now being part of the group
Additionally, existing custom group object attributes named _name_last_ and _parent_id_ will be renamed too, by adding
an underscore in front (_\_name_last_ and _\_parent_id_). This is due to these attributes now being part of the group
model, requiring dedicated table columns under the reserved names.
### Disallowed URL Values in User's Name Attributes

View File

@@ -1,8 +1,8 @@
# Change Log for Zammad 7.0.x
# Change Log for Zammad 7.0.3
- [Release notes](https://zammad.com/en/releases/7-0-x)
- [Release notes](https://zammad.com/en/product/releases/7-1-1-7-0-3)
- [Breaking changes](BREAKING_CHANGES.md#70)
- [Implemented enhancements](https://github.com/zammad/zammad/issues?q=is%3Aclosed+milestone%3A7.0+(-type%3ABug+AND+-label%3Abug))
- [Closed bugs](https://github.com/zammad/zammad/issues?q=is%3Aclosed+milestone%3A7.0+(type%3ABug+OR+label%3Abug))
- [Full commit log](https://github.com/zammad/zammad/compare/6.5.1...7.0.x)
- [File tree](https://github.com/zammad/zammad/tree/7.0.x)
- [Full commit log](https://github.com/zammad/zammad/compare/7.1.1...7.0.3)
- [File tree](https://github.com/zammad/zammad/tree/7.0.3)

View File

@@ -17,11 +17,14 @@ ENV RAILS_ENV="production" \
RAILS_LOG_TO_STDOUT="true"
# Install base packages
# Add official PostgreSQL apt repository to not depend on Debian's version. https://www.postgresql.org/download/linux/debian/ \
# Add official PostgreSQL apt repository to not depend on Debian's version.
# https://www.postgresql.org/download/linux/debian/
# Use `postgresql-client` meta-package to have the latest `pg_dump` that works even with the latest PostgreSQL versions.
# https://github.com/zammad/zammad/issues/6009
RUN apt-get update -qq && \
apt-get install -y postgresql-common && \
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
apt-get install --no-install-recommends -y curl libimlib2 libpq5 nginx gnupg postgresql-client-17 && \
apt-get install --no-install-recommends -y curl libimlib2 libpq5 nginx gnupg postgresql-client && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Throw-away stage to get the node binary

View File

@@ -265,7 +265,7 @@ group :development, :test do
gem 'localhost'
# Keycloak admin tool for setting up SAML auth tests
gem 'ruby-keycloak-admin', git: 'https://github.com/fliebe92/ruby-keycloak-admin'
gem 'ruby-keycloak-admin'
# Debugging and profiling
gem 'pry-doc' # This gem is very large, so don't include it in production.

View File

@@ -1,11 +1,3 @@
GIT
remote: https://github.com/fliebe92/ruby-keycloak-admin
revision: ef2cf3a24f98fdfad3aa1a4872bc63f52761a29e
specs:
ruby-keycloak-admin (26.0.8)
hashie (>= 5.0.0)
httparty (>= 0.21, < 0.25)
GIT
remote: https://github.com/zammad-deps/autodiscover
revision: ee9b53dfa797ce6d4f970b82beea7fbdd2df56bb
@@ -40,29 +32,29 @@ GEM
simple_po_parser (~> 1.1.6)
aasm (5.5.2)
concurrent-ruby (~> 1.0)
actioncable (8.0.4)
actionpack (= 8.0.4)
activesupport (= 8.0.4)
actioncable (8.0.5)
actionpack (= 8.0.5)
activesupport (= 8.0.5)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionmailbox (8.0.4)
actionpack (= 8.0.4)
activejob (= 8.0.4)
activerecord (= 8.0.4)
activestorage (= 8.0.4)
activesupport (= 8.0.4)
actionmailbox (8.0.5)
actionpack (= 8.0.5)
activejob (= 8.0.5)
activerecord (= 8.0.5)
activestorage (= 8.0.5)
activesupport (= 8.0.5)
mail (>= 2.8.0)
actionmailer (8.0.4)
actionpack (= 8.0.4)
actionview (= 8.0.4)
activejob (= 8.0.4)
activesupport (= 8.0.4)
actionmailer (8.0.5)
actionpack (= 8.0.5)
actionview (= 8.0.5)
activejob (= 8.0.5)
activesupport (= 8.0.5)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
actionpack (8.0.4)
actionview (= 8.0.4)
activesupport (= 8.0.4)
actionpack (8.0.5)
actionview (= 8.0.5)
activesupport (= 8.0.5)
nokogiri (>= 1.8.5)
rack (>= 2.2.4)
rack-session (>= 1.0.1)
@@ -70,27 +62,27 @@ GEM
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (8.0.4)
actionpack (= 8.0.4)
activerecord (= 8.0.4)
activestorage (= 8.0.4)
activesupport (= 8.0.4)
actiontext (8.0.5)
actionpack (= 8.0.5)
activerecord (= 8.0.5)
activestorage (= 8.0.5)
activesupport (= 8.0.5)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (8.0.4)
activesupport (= 8.0.4)
actionview (8.0.5)
activesupport (= 8.0.5)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activejob (8.0.4)
activesupport (= 8.0.4)
activejob (8.0.5)
activesupport (= 8.0.5)
globalid (>= 0.3.6)
activemodel (8.0.4)
activesupport (= 8.0.4)
activerecord (8.0.4)
activemodel (= 8.0.4)
activesupport (= 8.0.4)
activemodel (8.0.5)
activesupport (= 8.0.5)
activerecord (8.0.5)
activemodel (= 8.0.5)
activesupport (= 8.0.5)
timeout (>= 0.4.0)
activerecord-import (2.2.0)
activerecord (>= 4.2)
@@ -100,13 +92,13 @@ GEM
cgi (>= 0.3.6)
rack (>= 2.0.8, < 4)
railties (>= 7.0)
activestorage (8.0.4)
actionpack (= 8.0.4)
activejob (= 8.0.4)
activerecord (= 8.0.4)
activesupport (= 8.0.4)
activestorage (8.0.5)
actionpack (= 8.0.5)
activejob (= 8.0.5)
activerecord (= 8.0.5)
activesupport (= 8.0.5)
marcel (~> 1.0)
activesupport (8.0.4)
activesupport (8.0.5)
base64
benchmark (>= 0.3)
bigdecimal
@@ -122,8 +114,8 @@ GEM
acts_as_list (1.2.6)
activerecord (>= 6.1)
activesupport (>= 6.1)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
addressable (2.9.0)
public_suffix (>= 2.0.2, < 8.0)
aes_key_wrap (1.1.0)
android_key_attestation (0.3.0)
argon2 (2.3.3)
@@ -131,6 +123,8 @@ GEM
ffi-compiler (~> 1.0)
ast (2.4.3)
attr_required (1.0.2)
auth-sanitizer (0.2.1)
version_gem (~> 1.1, >= 1.1.10)
autoprefixer-rails (10.4.21.0)
execjs (~> 2)
aws-eventstream (1.4.0)
@@ -154,7 +148,7 @@ GEM
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (3.3.1)
bigdecimal (4.0.1)
bindata (2.5.1)
binding_of_caller (1.0.1)
debug_inspector (>= 1.2.0)
@@ -199,7 +193,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.3.6)
concurrent-ruby (1.3.7)
connection_pool (2.5.5)
cose (1.3.1)
cbor (~> 0.5.9)
@@ -237,8 +231,8 @@ GEM
dry-types (~> 1.8, >= 1.8.2)
ice_nine (~> 0.11)
zeitwerk (~> 2.6)
dry-types (1.8.3)
bigdecimal (~> 3.0)
dry-types (1.9.1)
bigdecimal (>= 3.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0)
dry-inflector (~> 1.0)
@@ -266,7 +260,7 @@ GEM
simpleidn
email_validator (2.2.4)
activemodel
erb (5.0.3)
erb (6.0.4)
erubi (1.13.1)
eventmachine (1.2.7)
execjs (2.10.0)
@@ -277,7 +271,7 @@ GEM
railties (>= 6.1.0)
faker (3.6.0)
i18n (>= 1.8.11, < 2)
faraday (2.14.0)
faraday (2.14.3)
faraday-net_http (>= 2.0, < 3.5)
json
logger
@@ -288,7 +282,7 @@ GEM
hashie
faraday-multipart (1.1.1)
multipart-post (~> 2.0)
faraday-net_http (3.4.2)
faraday-net_http (3.4.4)
net-http (~> 0.5)
ffi (1.17.3)
ffi-compiler (1.3.2)
@@ -299,7 +293,7 @@ GEM
ostruct
globalid (1.2.1)
activesupport (>= 6.1)
graphql (2.5.19)
graphql (2.5.26)
base64
fiber-storage
logger
@@ -309,12 +303,13 @@ GEM
graphql-fragment_cache (1.22.2)
graphql (>= 2.1.4)
hashdiff (1.2.1)
hashie (5.0.0)
hashie (5.1.0)
logger
hiredis-client (0.26.4)
redis-client (= 0.26.4)
htmlentities (4.4.2)
http_parser.rb (0.8.0)
httparty (0.24.0)
httparty (0.24.2)
csv
mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2)
@@ -322,7 +317,7 @@ GEM
mutex_m
i18n (1.14.8)
concurrent-ruby (~> 1.0)
icalendar (2.12.1)
icalendar (2.12.2)
base64
ice_cube (~> 0.16)
logger
@@ -342,7 +337,7 @@ GEM
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jmespath (1.6.2)
json (2.18.1)
json (2.19.9)
json-jwt (1.17.0)
activesupport (>= 4.2)
aes_key_wrap
@@ -350,7 +345,7 @@ GEM
bindata
faraday (~> 2.0)
faraday-follow_redirects
jwt (2.10.2)
jwt (2.10.3)
base64
koala (3.7.0)
addressable
@@ -404,7 +399,7 @@ GEM
nestful (1.1.4)
net-http (0.9.1)
uri (>= 0.11.1)
net-imap (0.6.2)
net-imap (0.6.4.1)
date
net-protocol
net-ldap (0.20.0)
@@ -418,25 +413,31 @@ GEM
net-protocol
nio4r (2.7.5)
nkf (0.2.0)
nokogiri (1.19.0)
nokogiri (1.19.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nori (2.7.1)
bigdecimal
oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1)
snaky_hash (~> 2.0)
version_gem (~> 1.1)
oauth-tty (1.0.5)
version_gem (~> 1.1, >= 1.1.1)
oauth2 (2.0.18)
oauth (1.1.6)
auth-sanitizer (~> 0.2, >= 0.2.1)
base64 (~> 0.1)
cgi
oauth-tty (~> 1.0, >= 1.0.8)
snaky_hash (~> 2.0, >= 2.0.5)
version_gem (~> 1.1, >= 1.1.11)
oauth-tty (1.0.8)
auth-sanitizer (~> 0.1, >= 0.1.3)
cgi
version_gem (~> 1.1, >= 1.1.9)
oauth2 (2.0.22)
auth-sanitizer (~> 0.2, >= 0.2.1)
faraday (>= 0.17.3, < 4.0)
jwt (>= 1.0, < 4.0)
logger (~> 1.2)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
snaky_hash (~> 2.0, >= 2.0.3)
version_gem (~> 1.1, >= 1.1.9)
snaky_hash (~> 2.0, >= 2.0.5)
version_gem (~> 1.1, >= 1.1.11)
omniauth (2.1.4)
hashie (>= 3.4.6)
logger
@@ -542,7 +543,7 @@ GEM
date
stringio
public_suffix (6.0.2)
puma (7.2.0)
puma (7.2.1)
nio4r (~> 2.0)
pundit (2.5.2)
activesupport (>= 3.0.0)
@@ -552,7 +553,7 @@ GEM
rspec-mocks (~> 3.12)
rspec-support (~> 3.12)
racc (1.8.1)
rack (2.2.21)
rack (2.2.23)
rack-attack (6.8.0)
rack (>= 1.0, < 4)
rack-oauth2 (2.2.1)
@@ -574,20 +575,20 @@ GEM
rackup (1.0.1)
rack (< 3)
webrick
rails (8.0.4)
actioncable (= 8.0.4)
actionmailbox (= 8.0.4)
actionmailer (= 8.0.4)
actionpack (= 8.0.4)
actiontext (= 8.0.4)
actionview (= 8.0.4)
activejob (= 8.0.4)
activemodel (= 8.0.4)
activerecord (= 8.0.4)
activestorage (= 8.0.4)
activesupport (= 8.0.4)
rails (8.0.5)
actioncable (= 8.0.5)
actionmailbox (= 8.0.5)
actionmailer (= 8.0.5)
actionpack (= 8.0.5)
actiontext (= 8.0.5)
actionview (= 8.0.5)
activejob (= 8.0.5)
activemodel (= 8.0.5)
activerecord (= 8.0.5)
activestorage (= 8.0.5)
activesupport (= 8.0.5)
bundler (>= 1.15.0)
railties (= 8.0.4)
railties (= 8.0.5)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
@@ -599,9 +600,9 @@ GEM
rails-html-sanitizer (1.6.2)
loofah (~> 2.21)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
railties (8.0.4)
actionpack (= 8.0.4)
activesupport (= 8.0.4)
railties (8.0.5)
actionpack (= 8.0.5)
activesupport (= 8.0.5)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
@@ -697,6 +698,9 @@ GEM
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec (~> 3.5)
ruby-keycloak-admin (26.5.2)
hashie (>= 5.0.0)
httparty (>= 0.21, < 0.25)
ruby-progressbar (1.13.0)
ruby-saml (1.18.1)
nokogiri (>= 1.13.10)
@@ -736,7 +740,7 @@ GEM
hashie
logger
slop (3.6.0)
snaky_hash (2.0.3)
snaky_hash (2.0.5)
hashie (>= 0.1.0, < 6)
version_gem (>= 1.1.8, < 3)
sprockets (3.7.5)
@@ -797,7 +801,7 @@ GEM
activemodel (>= 3.0.0)
public_suffix
vcr (6.4.0)
version_gem (1.1.9)
version_gem (1.1.11)
viewpoint (1.2.0)
httpclient
logging
@@ -845,7 +849,7 @@ GEM
rubyzip (>= 2.4.0, < 4.0)
xpath (3.2.0)
nokogiri (~> 1.8)
yard (0.9.38)
yard (0.9.43)
zeitwerk (2.7.4)
zendesk_api (3.1.1)
faraday (> 2.0.0)
@@ -962,7 +966,7 @@ DEPENDENCIES
rubocop-rake
rubocop-rspec
rubocop-rspec_rails
ruby-keycloak-admin!
ruby-keycloak-admin
sass-rails (~> 5)
selenium-webdriver
shoulda-matchers

View File

@@ -1 +1 @@
7.0.x
7.0.3

View File

@@ -1,6 +1,13 @@
class ChannelAiProvider extends App.ControllerTabs
@requiredPermission: 'admin.ai_provider'
header: __('Provider')
headerSwitchName: 'ai_provider'
events:
'change .js-header-switch input': 'didChangeHeaderSwitch'
elements:
'.js-header-switch input': 'aiProviderSetting'
constructor: ->
super
@@ -20,6 +27,44 @@ class ChannelAiProvider extends App.ControllerTabs
@render()
@controllerBind('config_update', @aiProviderConfigHasChanged)
aiProviderConfigHasChanged: (config) =>
return if config.name isnt 'ai_provider'
@renderHeader(config.value)
render: =>
super
@renderHeader(App.Config.get('ai_provider'))
renderHeader: (value) =>
@aiProviderSetting.prop('checked', value)
didChangeHeaderSwitch: ->
value = @aiProviderSetting.prop('checked')
App.Setting.set('ai_provider', value, done: =>
# If the provider is being enabled but not yet configured, disable it again and show a warning message.
if value and not @controllerList[0].form?.current_provider
App.Setting.set('ai_provider', false, done: =>
@renderHeader(false)
@notify(
type: 'warning'
msg: __('Please set up the provider before proceeding under the settings tab.')
)
)
return
@notify(
type: 'success'
msg: if value
__('AI provider enabled successfully.')
else
__('AI provider disabled successfully.')
)
)
class AiProviderSettings extends App.Controller
@requiredPermission: 'admin.ai_provider'
description : __('This service allows you to connect Zammad with an AI provider.')
@@ -32,11 +77,17 @@ class AiProviderSettings extends App.Controller
release: =>
App.Setting.unsubscribe(@subscribeId)
render: =>
render: (_changedItems, localOrServer) =>
return if localOrServer and localOrServer isnt 'refresh'
return if _.isEqual(@aiProviderConfigAtRender, App.Setting.get('ai_provider_config'))
@aiProviderConfigAtRender = _.clone(App.Setting.get('ai_provider_config'))
@html App.view('ai/provider')(
description: @description,
)
new ProviderForm()
@form = new ProviderForm()
class AiProviderFeedbackAndLogs extends App.Controller
@requiredPermission: 'admin.ai_provider'
@@ -103,11 +154,7 @@ class AiProviderFeedbackAndLogs extends App.Controller
@httpLog = null
super
class ProviderForm extends App.Controller
events:
'.js-provider-submit': 'update'
constructor: (content) ->
super
@@ -116,7 +163,6 @@ class ProviderForm extends App.Controller
@render(content)
getSortedProviderOptions: ->
Object
.entries(@providers)
@@ -135,7 +181,7 @@ class ProviderForm extends App.Controller
type: 'password'
single: true
null: not _.contains(provider.required, 'token')
autocomplete: 'off'
autocomplete: 'new-password'
value: params.token
}
model: {
@@ -244,9 +290,9 @@ class ProviderForm extends App.Controller
render: (provider) ->
config = App.Setting.get('ai_provider_config') || {}
current_provider = if provider isnt undefined then provider else config['provider']
@current_provider = if provider isnt undefined then provider else config['provider']
configure_attributes = @providerConfiguration(current_provider, config)
configure_attributes = @providerConfiguration(@current_provider, config)
@providerSettingsForm?.releaseController()
@providerSettingsForm = new App.ControllerForm(
@@ -259,6 +305,7 @@ class ProviderForm extends App.Controller
)
$('.js-provider-submit').off('click.provider').on('click.provider', @update)
$('select[name=provider]').off('change.provider').on('change.provider', (e) =>
@render($(e.target).val())
)
@@ -279,6 +326,8 @@ class ProviderForm extends App.Controller
@validateAndSave(params)
validateAndSave: (params) ->
App.ControllerForm.disable(@providerSettingsForm.form)
has_provider = not _.isEmpty(params.provider)
if not has_provider
@@ -294,8 +343,31 @@ class ProviderForm extends App.Controller
if has_provider && !params.hasOwnProperty('token') && savedProviderConfig.provider == params.provider && savedProviderConfig.token
params.token = savedProviderConfig.token
App.Setting.set('ai_provider_config', params, done: ->
App.Setting.set('ai_provider', has_provider, notify: true)
App.Setting.set(
'ai_provider_config',
params,
done: =>
App.ControllerForm.enable(@providerSettingsForm.form)
# If the provider configuration is being updated, do not touch the provider switch.
if has_provider
App.Event.trigger 'notify', {
type: 'success'
msg: __('Update successful.')
timeout: 2000
}
return
# Turn off the provider switch when the provider configuration is emptied.
App.Setting.set('ai_provider', false, done: =>
@notify(
type: 'success'
msg: __('AI provider disabled successfully.')
)
)
fail: =>
App.ControllerForm.enable(@providerSettingsForm.form)
)
App.Config.set('Provider', { prio: 1000, name: __('Provider'), parent: '#ai', target: '#ai/provider', controller: ChannelAiProvider, permission: ['admin.ai_provider'] }, 'NavBarAdmin')

View File

@@ -54,6 +54,7 @@ class App.TicketSummary extends App.ControllerAIFeatureBase
value: 'on_ticket_summary_sidebar_activation'
},
]
translate: true
)
content.find('.js-ticketSummaryGenerationConfigSelect').html(select)

View File

@@ -73,10 +73,14 @@ class App.GenericHistory extends App.ControllerModal
created_by: App.User.find( item.created_by_id )
sources: []
recordsSource = _.findWhere(newItem.sources, { sourceable_id: item.sourceable_id })
recordsSource = _.findWhere(newItem.sources, {
sourceable_id: item.sourceable_id,
sourceable_type_raw: item.sourceable_type
})
if !recordsSource
recordsSource = {
sourceable_id: item.sourceable_id,
sourceable_type_raw: item.sourceable_type,
sourceable_type: @sourceableTypeDisplayName(item.sourceable_type),
sourceable_name: item.sourceable_name,
users: []

View File

@@ -161,7 +161,7 @@ class App.ControllerForm extends App.Controller
container = $("<div class='form-buttons #{@fullFormButtonsContainerClass}'>")
for buttonConfig in @fullFormAdditionalButtons
btn = $("<button class='btn #{buttonConfig.className}'>").text(buttonConfig.text)
btn = $("<button class='btn #{buttonConfig.className}'>").text(App.i18n.translateContent(buttonConfig.text))
if buttonConfig.disabled
btn.prop('disabled', true)
container.append(btn)

View File

@@ -693,7 +693,7 @@ class App.ControllerTable extends App.Controller
true
callback: (id) =>
item = @model.find(id)
item.name = "Clone: #{item.name}"
item.name = App.i18n.translatePlain('Clone: %s', item.name)
if @cloneCallback
@cloneCallback(item)

View File

@@ -252,7 +252,7 @@ class ChannelChat extends App.ControllerSubContent
renderError: (data) =>
errorMessage = data?.error || __('An error occurred while fetching the website preview.')
@browserHelp.text(App.i18n.translateInline(errorMessage))
@browserHelp.text(App.i18n.translatePlain(errorMessage))
.show()
@urlInput.removeClass('is-loading')

View File

@@ -326,9 +326,6 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
'click .js-goToSlide': 'goToSlide'
'click .js-expert': 'probeBasedOnIntro'
'click .js-close': 'hide'
inboundPassword: ''
outboundPassword: ''
passwordPlaceholder: '{{{{{{{{{{{{SECRTE_PASSWORD}}}}}}}}}}}}'
constructor: ->
super
@@ -349,14 +346,6 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
outbound: clone(@channel.options.outbound)
meta: {}
# remember passwords, do not show in ui
if @account.inbound.options && @account.inbound.options.password
@inboundPassword = @account.inbound.options.password
@account.inbound.options.password = @passwordPlaceholder
if @account.outbound.options && @account.outbound.options.password
@outboundPassword = @account.outbound.options.password
@account.outbound.options.password = @passwordPlaceholder
@render()
if @channel
@@ -645,9 +634,6 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
@formValidate(form: e.target, errors: errors)
return false
if params.options && params.options.password is @passwordPlaceholder
params.options.password = @inboundPassword
# Update meta as the one from AttributesBase could be outdated
@account.meta.realname = params.options.realname
@account.meta.email = params.options.email
@@ -809,9 +795,6 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
params = @formParam(e.target)
params['email'] = @account['meta']['email']
if params.options && params.options.password is @passwordPlaceholder
params.options.password = @outboundPassword
if !params['email'] && @channel
email_addresses = App.EmailAddress.search(filter: { channel_id: @channel.id })
if email_addresses && email_addresses[0]
@@ -883,11 +866,6 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
if email_addresses && email_addresses[0]
params.email = email_addresses[0].email
if params.inbound?.options?.password is @passwordPlaceholder
params.inbound.options.password = @inboundPassword
if params.outbound?.options?.password is @passwordPlaceholder
params.outbound.options.password = @outboundPassword
@ajax(
id: 'email_verify'
type: 'POST'
@@ -938,9 +916,6 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
'change .js-outbound [name=adapter]': 'toggleOutboundAdapter'
'submit .js-outbound': 'probleOutbound'
'click .js-close': 'hide'
inboundPassword: ''
outboundPassword: ''
passwordPlaceholder: '{{{{{{{{{{{{SECRTE_PASSWORD}}}}}}}}}}}}'
constructor: ->
super
@@ -960,14 +935,6 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
inbound: clone(@channel.options.inbound)
outbound: clone(@channel.options.outbound)
# remember passwords, do not show in ui
if @account.inbound && @account.inbound.options && @account.inbound.options.password
@inboundPassword = @account.inbound.options.password
@account.inbound.options.password = @passwordPlaceholder
if @account.outbound && @account.outbound.options && @account.outbound.options.password
@outboundPassword = @account.outbound.options.password
@account.outbound.options.password = @passwordPlaceholder
@render()
@toggleSslVerifyAlert(target: @el.find('[name="options::ssl_verify"]'))
@@ -1059,9 +1026,6 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
# get params
params = @formParam(e.target)
if params.options && params.options.password is @passwordPlaceholder
params.options.password = @outboundPassword
# let backend know about the channel
params.channel_id = @channel.id

View File

@@ -131,6 +131,9 @@ class WhatsappAccountCloudAPIModal extends App.ControllerModal
params = if @params then _.extend(@params, @formParams()) else @formParams()
if @channel
params.channel_id = @channel.id
@ajax(
id: 'whatsapp_initial'
type: 'POST'

View File

@@ -61,7 +61,7 @@ class Form extends App.Controller
if !@config
@config = @currentConfig()
settings = [
{ name: 'api_key', display: __('API Key'), tag: 'input', type: 'text', limit: 100, null: false, placeholder: '...', note: __('Your API key.') },
{ name: 'api_key', display: __('API Key'), tag: 'input', type: 'text', input_type: 'password', limit: 100, null: false, placeholder: '…', note: __('Your API key.') },
{ name: 'organization_autocreate', display: __('Auto create'), tag: 'boolean', type: 'boolean', null: false, note: __('Create organizations automatically if record has one.') },
{ name: 'organization_shared', display: __('Shared'), tag: 'boolean', type: 'boolean', null: false, note: __('New organizations are shared.') },
]

View File

@@ -2,7 +2,7 @@ class Ldap extends App.ControllerIntegrationBase
featureIntegration: 'ldap_integration'
featureName: __('LDAP')
description: [
[__('Use this switch to start synchronization of your ldap sources.')]
[__('Use this switch to start synchronization of your LDAP sources.')]
[__('If a user is found in two (or more) configured LDAP sources, the last synchronisation will win.')]
[__('In order to be able to influence the desired behavior in this regard, you can influence the order of the LDAP sources via drag & drop.')]
]
@@ -182,7 +182,7 @@ class Form extends App.Controller
setConfig: (value) =>
@item.name = value.name
@item.active = value.active
@item.preferences = _.omit(value, ['id', 'name', 'active'])
@item.preferences = _.omit(value, ['id', 'name', 'active', 'ldap_source_id'])
@item.save(
done: =>
@showIndex()
@@ -521,6 +521,7 @@ class ConnectionWizard extends App.ControllerWizardModal
params.host = @wizardConfig.host
params.ssl = @wizardConfig.ssl
params.ssl_verify = @wizardConfig.ssl_verify
params.ldap_source_id = @wizardConfig.id if @wizardConfig.id
@ajax(
id: 'ldap_bind'
type: 'POST'
@@ -820,7 +821,7 @@ class LdapSourceIndex extends App.ControllerGenericIndex
item = new App.LdapSource(
name: config.name
active: config.active
preferences: _.omit(config, ['id', 'name', 'active'])
preferences: _.omit(config, ['id', 'name', 'active', 'ldap_source_id'])
)
item.save(
done: ->

View File

@@ -26,6 +26,13 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
@render()
@fetchAndRender()
@controllerBind('config_update_local', @kbActiveConfigChanged)
kbActiveConfigChanged: (data) =>
return if data.name isnt 'kb_active'
return if !@knowledge_base_id
@headerSwitchInput.prop('checked', data.value)
fetchAndRender: =>
@startLoading()

View File

@@ -5,18 +5,23 @@ class Maintenance extends App.Controller
@controllerBind(
'maintenance'
(data) =>
if data.type is 'message'
@showMessage(data)
if data.type is 'mode'
@maintanaceMode(data)
if data.type is 'app_version'
@maintanaceAppVersion(data)
if data.type is 'config_changed'
@maintanaceConfigChanged(data)
if data.type is 'restart_auto'
@maintanaceRestartAuto(data)
if data.type is 'restart_manual'
@maintanaceRestartManual(data)
switch data.type
when 'message'
@showMessage(data)
when 'mode'
@maintenanceMode(data)
when 'app_version'
@maintenanceAppVersion(data)
when 'config_changed'
@maintenanceConfigChanged(data)
when 'invalid_csrf_token'
@maintenanceInvalidCSRFToken(data)
when 'restart_auto'
@maintenanceRestartAuto(data)
when 'restart_manual'
@maintenanceRestartManual(data)
when 'force_refresh'
@maintenanceForceRefresh(data)
)
showMessage: (message = {}) =>
@@ -40,13 +45,13 @@ class Maintenance extends App.Controller
forceReload: message.reload
)
maintanaceMode: (data = {}) =>
maintenanceMode: (data = {}) =>
return if data.on isnt true
return if !@authenticateCheck()
@navigate '#logout'
#App.Event.trigger('maintenance', {type:'restart_auto'})
maintanaceRestartAuto: (data) =>
maintenanceRestartAuto: (data) =>
return if @messageRestartAuto
App.SessionStorage.clear()
@@ -65,7 +70,7 @@ class Maintenance extends App.Controller
@checkAvailability()
#App.Event.trigger('maintenance', {type:'restart_manual'})
maintanaceRestartManual: (data) =>
maintenanceRestartManual: (data) =>
return if @messageRestartManual
App.SessionStorage.clear()
@@ -83,7 +88,13 @@ class Maintenance extends App.Controller
@disconnectClient()
@checkAvailability()
maintanaceConfigChanged: (data) =>
maintenanceForceRefresh: (data) =>
return if @forceRefreshed
@forceRefreshed = true
window.location.reload()
maintenanceConfigChanged: (data) =>
return if @messageConfigChanged
App.SessionStorage.clear()
@@ -98,7 +109,23 @@ class Maintenance extends App.Controller
forceReload: true
)
maintanaceAppVersion: (data) =>
maintenanceInvalidCSRFToken: (data) =>
return if @messageConfigChanged
App.SessionStorage.clear()
@messageConfigChanged = new App.SessionMessage(
head: __('Security token verification failed')
message: __('The included security token is invalid. This may be just a temporary error, please reload your browser, and try again.')
keyboard: false
backdrop: true
buttonClose: false
buttonSubmit: __('Reload')
forceReload: true
reloadTimeout: 10000
)
maintenanceAppVersion: (data) =>
return if @messageAppVersion
return if @appVersion is data.app_version
if !@appVersion

View File

@@ -182,7 +182,7 @@ class Navigation extends App.Controller
new App.ControllerConfirm(
head: __('Help')
message: __('You can switch between the old and the New BETA UI at any moment in the Profile > New BETA UI section.')
message: __('You can switch between the old and the New BETA UI at any moment in the Profile settings > New BETA UI section.')
buttonClass: 'btn--success'
buttonCancel: false
buttonSubmit: __('Got it')
@@ -190,8 +190,9 @@ class Navigation extends App.Controller
)
content.find('.js-switchControl').replaceWith App.UiElement.switch.render(
name: 'desktop_beta_switch'
name: 'desktop_beta_switch'
display: __('Try New BETA UI')
theme: 'light' # force light mode on the switch, due to bad contrast in dark mode
)
content.find('input[name="desktop_beta_switch"]')

View File

@@ -36,9 +36,18 @@ class App.SettingsAreaSwitch extends App.Controller
subSetting: @subSetting
)
for localSetting in @subSetting
directValue = 0
for item in localSetting.options['form']
directValue += 1
if directValue > 1
params = localSetting.state_current.value
else
params = {}
params[localSetting.options['form'][0].name] = localSetting.state_current.value
new App.ControllerForm(
el: @$('.form-item')
params: localSetting.state_current.value
params: params
model: { configure_attributes: localSetting.options['form'], className: '' }
autofocus: false
)

View File

@@ -14,9 +14,10 @@ class App.UiElement.autocompletion_ajax
name: attribute.name
id: params.organization_id || attribute.id
placeholder: App.i18n.translateInline('Search…')
limit: 40
relation: attribute.relation
ajax: true
multiple: attribute.multiple
limit: 40
relation: attribute.relation
ajax: true
multiple: attribute.multiple
allowInactive: attribute.allow_inactive
)
searchableAjaxSelectObject.element()

View File

@@ -269,17 +269,18 @@ class App.UiElement.object_attribute_options_context extends Spine.Module
flatOptions = []
options.forEach (option) ->
label = if isTree
App.TokenHelper.computeNameValue(option.value, isTranslated)
else if isTranslated
App.i18n.translateInline(option.name or option.value)
else
option.name or option.value
if !option.disabled
label = if isTree
App.TokenHelper.computeNameValue(option.value, isTranslated)
else if isTranslated
App.i18n.translateInline(option.name or option.value)
else
option.name or option.value
flatOptions.push(
value: option.value
label: label
)
flatOptions.push(
value: option.value
label: label
)
if option.children
flatOptions.push App.UiElement.object_attribute_options_context.buildFlatOptions(

View File

@@ -3,38 +3,28 @@ class TicketCreateFormHandlerSignature
@run: (params, attribute, attributes, classname, form, ui) ->
return if !attribute
return if attribute.name isnt 'group_id'
signature = undefined
if params['group_id']
group = App.Group.find(params['group_id'])
if group && group.signature_id
signature = App.Signature.find(group.signature_id)
result = App.SignatureHelper.findForGroup(params['group_id'])
# check if signature needs to be added
type = ui.el.closest('.content').find('[name="formSenderType"]').val()
if signature && signature.active && signature.body && type is 'email-out'
signatureFinished = App.Utils.replaceTags(signature.body,
user: App.Session.get()
config: App.Config.all()
# Fake a ticket object, if a group is present (#4448).
ticket:
group: group
)
if result && type is 'email-out'
currentBody = ui.el.closest('.content').find('[data-name=body]')
if !_.isEmpty(currentBody)
# Always remove any existing signature first (from old or new editor format)
# before checking and potentially adding a new one
ui.el.closest('.content').find('[data-signature="true"]').remove()
return if _.isEmpty(currentBody)
# Refresh currentBody reference after removal
currentBody = ui.el.closest('.content').find('[data-name=body]')
if App.Utils.signatureCheck(currentBody.html() || '', signatureFinished)
signature = $("<div data-signature=\"true\" data-signature-id=\"#{signature.id}\">#{signatureFinished}</div>")
App.Utils.htmlStrip(signature)
currentBody.append(signature)
ui.el.closest('.content').find('[data-name=body]').replaceWith(currentBody)
# skip re-applying the signature if it is already present (e.g. after autosave restore)
return if currentBody.find("[data-signature-id=\"#{result.signature.id}\"]").length
# remove existing signature and re-add it
# https://github.com/zammad/zammad/issues/2319
App.SignatureHelper.removeTopLevel(currentBody)
# Fake a ticket object, if a group is present (#4448).
signatureFinished = App.SignatureHelper.render(result.signature.body, { group: result.group })
sigEl = App.SignatureHelper.buildElement(result.signature.id, signatureFinished)
App.SignatureHelper.appendToBottom(currentBody, sigEl)
ui.el.closest('.content').find('[data-name=body]').replaceWith(currentBody)
# remove old signature
else

View File

@@ -35,7 +35,7 @@ class Success extends App.ControllerAppContent
detail: __('Woo hoo! Your email address has been verified!')
)
delay = =>
@navigate '#'
App.Auth.loginCheck(=> @navigate '#')
@delay(delay, 2000)
class Fail extends App.ControllerAppContent

View File

@@ -151,19 +151,18 @@ class ImportFreshdesk extends App.ControllerWizardFullScreen
url: "#{@apiPath}/import/freshdesk/import_status"
processData: true
success: (data, status, xhr) =>
if _.isEmpty(data.result) && @updateMigrationDisplayLoop > 16
@$('.js-error').removeClass('hide')
@$('.js-error').html(App.i18n.translateContent('Background process did not start or has not finished! Please contact your support.'))
return
if !_.isEmpty(data.result['error'])
if !_.isEmpty(data.result) && !_.isEmpty(data.result['error'])
@$('.js-error').removeClass('hide')
@$('.js-error').html(App.i18n.translateContent(data.result['error']))
else
@$('.js-error').addClass('hide')
if !_.isEmpty(data.finished_at) && _.isEmpty(data.result['error'])
if data.setup_done is true
@redirectToLogin()
return

View File

@@ -158,13 +158,13 @@ class ImportKayako extends App.ControllerWizardFullScreen
@$('.js-error').html(App.i18n.translateContent('Background process did not start or has not finished! Please contact your support.'))
return
if !_.isEmpty(data.result['error'])
if !_.isEmpty(data.result) && !_.isEmpty(data.result['error'])
@$('.js-error').removeClass('hide')
@$('.js-error').html(App.i18n.translateContent(data.result['error']))
else
@$('.js-error').addClass('hide')
if !_.isEmpty(data.finished_at) && _.isEmpty(data.result['error'])
if data.setup_done is true
@redirectToLogin()
return

View File

@@ -107,11 +107,12 @@ class ImportOtrs extends App.ControllerWizardFullScreen
url: "#{@apiPath}/import/otrs/import_check"
processData: true
success: (data, status, xhr) =>
if data.result is 'ok'
if data.result? and data.result is 'ok'
@startMigration()
return
for issue in data.issues
@$(".js-#{issue}").removeClass('hide')
if data.issues?
for issue in data.issues
@$(".js-#{issue}").removeClass('hide')
@showImportNotice()
)
@@ -139,7 +140,7 @@ class ImportOtrs extends App.ControllerWizardFullScreen
processData: true
success: (data, status, xhr) =>
if data.result is 'import_done'
if data.setup_done is true
window.location.reload()
return

View File

@@ -156,13 +156,13 @@ class ImportZendesk extends App.ControllerWizardFullScreen
@$('.js-error').html(App.i18n.translateContent('Background process did not start or has not finished! Please contact your support.'))
return
if !_.isEmpty(data.result['error'])
if !_.isEmpty(data.result) && !_.isEmpty(data.result['error'])
@$('.js-error').removeClass('hide')
@$('.js-error').html(App.i18n.translateContent(data.result['error']))
else
@$('.js-error').addClass('hide')
if !_.isEmpty(data.finished_at) && _.isEmpty(data.result['error'])
if data.setup_done is true
@redirectToLogin()
return

View File

@@ -64,7 +64,11 @@ class App.KnowledgeBaseReaderListContainer.Categories extends App.KnowledgeBaseR
#
if !@isEditor
items = items.filter (elem) => elem.visibleInternally(@kb_locale)
items = items.filter (elem) =>
if elem.access(@kb_locale) == 'editor'
true
else
elem.visibleInternally(@kb_locale)
items

View File

@@ -124,15 +124,7 @@ class App.TextToolsModal extends App.ControllerModal
)
onSubmit: =>
if @feedbackWidget
@feedbackWidget.recordUsage(context: { approved: true }, null, =>
@notify(
type: 'error'
msg: __('Your AI result usage could not be recorded.')
)
false
)
@feedbackWidget.recordUsage(context: { approved: true }, null, -> false) if @feedbackWidget
@approve(@result)
@close()

View File

@@ -141,12 +141,10 @@ class App.TicketZoom extends App.Controller
)
)
load: (data, local = false, newTicketRaw = undefined) =>
load: (data, local = false) =>
@previousTicketRaw = @currentTicketRaw
@currentTicketRaw = data.assets.Ticket[@ticket_id]
return if newTicketRaw
view = @ticket?.currentView()
readable = @ticket?.userGroupAccess('read')
changeable = @ticket?.userGroupAccess('change')
@@ -1176,7 +1174,7 @@ class App.TicketZoom extends App.Controller
# reset form after save
@reset()
@load(data, false, true)
@load(data, false)
if @sidebarWidget
@sidebarWidget.commit()

View File

@@ -149,7 +149,8 @@ class EmailReply extends App.Controller
if selected
quote_header = @replyQuoteHeader(article)
selected = "<div><br><br/></div><div><blockquote type=\'cite\'>#{quote_header}#{selected}<br></blockquote></div><div><br></div>"
marker = if signaturePosition is 'top' then ' data-marker=\'signature-before\'' else ''
selected = "<div><br><br/></div><div><blockquote type=\'cite\'#{marker}>#{quote_header}#{selected}<br></blockquote></div><div><br></div>"
# add selected text to body
body = selected + body
@@ -258,7 +259,7 @@ class EmailReply extends App.Controller
quote_header = App.FullQuoteHeader.fullQuoteHeaderForward(article)
body = "<br/><div>---#{App.i18n.translateInline('Begin forwarded message')}:---<br/><br/></div><div><blockquote type=\"cite\">#{quote_header}#{body}</blockquote></div><div><br></div>"
body = "<br/><div>---#{App.i18n.translateInline('Begin forwarded message')}:---<br/><br/></div><div><blockquote type=\"cite\" data-marker=\"signature-before\">#{quote_header}#{body}</blockquote></div><div><br></div>"
articleNew = {}
articleNew.body = body
@@ -332,10 +333,8 @@ class EmailReply extends App.Controller
task = App.TaskManager.get(ui.taskKey)
if task && task.state && task.state.ticket && task.state.ticket.group_id
group_id = task.state.ticket.group_id
group = App.Group.find(group_id)
signature = undefined
if group && group.signature_id
signature = App.Signature.find(group.signature_id)
result = App.SignatureHelper.findForGroup(group_id)
# remove signature if it was added but type is no longer email
# https://github.com/zammad/zammad/issues/4453
@@ -344,34 +343,39 @@ class EmailReply extends App.Controller
return
# add/replace signature
if signature && signature.active && signature.body
if result
# if signature has changed, remove it but skip signatures in quoted messages
# https://github.com/zammad/zammad/issues/5634
signature_selector = ui.$('[data-signature=true]').not('blockquote [data-signature=true]')
signature_id = signature_selector.data('signature-id')
if signature_id && signature_id.toString() isnt signature.id.toString()
signature_selector.remove()
existingTopLevelSignature = ui.$('[data-signature=true]').not('blockquote [data-signature=true]').first()
if existingTopLevelSignature.length > 0 && existingTopLevelSignature.attr('data-signature-id') is "#{result.signature.id}"
unless signaturePosition is 'top'
App.Utils.htmlImage2DataUrlAsyncInline(ui.$('[contenteditable=true]'))
return
# remove existing top-level signature (skip signatures in quoted messages)
# https://github.com/zammad/zammad/issues/5634, https://github.com/zammad/zammad/issues/2319
App.SignatureHelper.removeTopLevel(ui.$('[data-name=body]'))
# apply new signature
signatureFinished = App.Utils.replaceTags(signature.body, { user: App.Session.get(), ticket: ticketCurrent, config: App.Config.all() })
signatureFinished = App.SignatureHelper.render(result.signature.body, ticketCurrent)
body = ui.$('[data-name=body]')
if App.Utils.signatureCheck(body.html() || '', signatureFinished)
if !App.Utils.htmlLastLineEmpty(body)
body.append('<br><br>')
signature = $("<div data-signature=\"true\" data-signature-id=\"#{signature.id}\">#{signatureFinished}</div>")
App.Utils.htmlStrip(signature)
sigEl = App.SignatureHelper.buildElement(result.signature.id, signatureFinished)
placeholder = body.find('[data-signature-placeholder]')
if placeholder.length > 0
placeholder[0].replaceWith(signature[0])
else if signaturePosition is 'top'
body.prepend(signature)
placeholder = body.find('[data-signature-placeholder]')
if placeholder.length > 0
placeholder[0].replaceWith(sigEl[0])
else
# detect full-quote layout on autosave restore: check data-marker first, then leading BR fingerprint
effectiveSigPosition = signaturePosition
if not signaturePosition? && (body.find('blockquote[type=cite][data-marker=signature-before]').length > 0 || EmailReply.hasFullQuoteLayout(body))
effectiveSigPosition = 'top'
if effectiveSigPosition is 'top'
body.prepend(sigEl)
body.prepend('<br><br>')
else
body.append(signature)
ui.$('[data-name=body]').replaceWith(body)
App.SignatureHelper.appendToBottom(body, sigEl)
ui.$('[data-name=body]').replaceWith(body)
else
body = ui.$('[data-name=body]')
@@ -394,6 +398,61 @@ class EmailReply extends App.Controller
# convert remote images into data urls
App.Utils.htmlImage2DataUrlAsyncInline(ui.$('[contenteditable=true]'))
# returns true when the body starts with a bare BR before a blockquote (full-quote fingerprint)
@hasFullQuoteLayout: (body) ->
return false unless body.find('blockquote[type=cite]').length > 0
firstChild = body.get(0)?.firstChild
firstChild = firstChild.nextSibling while firstChild?.nodeType is 3
firstChild?.nodeName is 'BR'
@updateSignatureByGroup: (type, ticket, ui, newGroupId) ->
return if type isnt 'email'
ticketCurrent = App.Ticket.fullLocal(ticket.id)
result = App.SignatureHelper.findForGroup(newGroupId)
body = ui.$('[data-name=body]')
if result
# apply new signature
signatureFinished = App.SignatureHelper.render(result.signature.body, ticketCurrent)
newSig = App.SignatureHelper.buildElement(result.signature.id, signatureFinished)
existingSignature = body.find('[data-signature=true]').not('blockquote [data-signature=true]').first()
if existingSignature.length > 0
# preserve signature in place if it already matches the new group (e.g. no actual change)
if existingSignature.attr('data-signature-id') is "#{result.signature.id}"
App.Utils.htmlImage2DataUrlAsyncInline(ui.$('[contenteditable=true]'))
return
# replace in-place to preserve the signature's position in the body
existingSignature.replaceWith(newSig[0])
else
# place signature before the full-quote block if marked, append otherwise
markedBlockquote = body.find('blockquote[type=cite][data-marker=signature-before]').first()
if markedBlockquote.length > 0
# place before the full-quote's container
parents = markedBlockquote.parentsUntil(body)
quoteContainer = if parents.length > 0 then parents.last() else markedBlockquote
prevElement = quoteContainer.prev()
if prevElement.length > 0
prevElement.before(newSig)
else
quoteContainer.before(newSig)
# add blank lines above signature if not already present
unless newSig[0].previousSibling?.nodeName is 'BR'
newSig.before('<br><br>')
else
App.SignatureHelper.appendToBottom(body, newSig)
else
# no signature for the new group – remove the existing one
App.SignatureHelper.removeTopLevel(body)
ui.$('[data-name=body]').replaceWith(body)
# convert remote images into data urls
App.Utils.htmlImage2DataUrlAsyncInline(ui.$('[contenteditable=true]'))
@validation: (type, params, ui) ->
return true if type isnt 'email'

View File

@@ -123,6 +123,13 @@ class App.TicketZoomArticleNew extends App.Controller
@updateSecurityOptions()
)
# update signature when group changes
@controllerBind('ui::ticket::updateSignature', (data) =>
return if data.taskKey isnt @taskKey
@updateSignatureByGroup(data.newGroupId)
)
# Listen to security setting changes.
@controllerBind('config_update', (data) =>
return if not /^(pgp|smime)_integration$/.test(data.name)
@@ -558,13 +565,18 @@ class App.TicketZoomArticleNew extends App.Controller
@updateSecurityTypeToolbar()
setArticleTypePost: (type, signaturePosition = 'bottom') =>
setArticleTypePost: (type, signaturePosition = undefined) =>
for localConfig in @actions()
if localConfig && localConfig.setArticleTypePost
localConfig.setArticleTypePost(@type, @ticket, @, signaturePosition)
@evaluateAttachmentsList()
updateSignatureByGroup: (newGroupId) =>
for localConfig in @actions()
if localConfig && localConfig.updateSignatureByGroup
localConfig.updateSignatureByGroup(@type, @ticket, @, newGroupId)
isScrolledToBottom: ->
return @el.scrollParent().scrollTop() + @el.scrollParent().height() is @el.scrollParent().prop('scrollHeight')

View File

@@ -0,0 +1,11 @@
class TicketZoomFormHandlerSignature
# central method, is getting called on every ticket form change
# but only trigger event for group_id changes
@run: (params, attribute, attributes, classname, form, ui) ->
return if attribute.name isnt 'group_id'
App.Event.trigger('ui::ticket::updateSignature', { taskKey: ui.taskKey, newGroupId: params.group_id })
App.Config.set('130-ticketFormSignature', TicketZoomFormHandlerSignature, 'TicketZoomFormHandler')

View File

@@ -132,10 +132,6 @@ class App.SidebarTicketSummary extends App.Controller
@badgeRenderLocal()
@feedbackWidget?.recordUsage({}, null, =>
@hasUsage = false
@notify(
type: 'error'
msg: __('Your AI result usage could not be recorded.')
)
false
)
@hasUsage = true
@@ -177,6 +173,8 @@ class App.SidebarTicketSummary extends App.Controller
renderSummarization: (data) =>
@summaryData = data if data
@isPreparingData = (_.isNull(@summaryData?.result) or @summaryData?.error)
@badgeRenderLocal()
return if !@elSidebar

View File

@@ -1,6 +1,36 @@
class App.SessionMessage extends App.ControllerModal
FORCE_RELOAD_TIMEOUT: 1000
showTrySupport: true
constructor: (params) ->
@initForcedReloadInterval(params)
super
initForcedReloadInterval: (params) =>
return if not params.forceReload or not params.reloadTimeout
buttonSubmit = params.buttonSubmit || __('Reload')
# Override default button label to show initial remaining time.
params.buttonSubmit = @buttonSubmitWithReload(buttonSubmit, params.reloadTimeout)
@totalDisplayTime = -1000 # account for try delay from modal subclass
callback = =>
@totalDisplayTime += @FORCE_RELOAD_TIMEOUT
timeLeft = params.reloadTimeout - @totalDisplayTime
$('.js-submit').text(@buttonSubmitWithReload(buttonSubmit, timeLeft))
@windowReload() if @totalDisplayTime >= params.reloadTimeout
@interval(callback, @FORCE_RELOAD_TIMEOUT, 'session_message_force_reload')
buttonSubmitWithReload: (buttonSubmit, timeLeft) ->
suffix = if timeLeft >= 0 then " (#{Math.ceil(timeLeft / 1000)})" else ''
App.i18n.translateInline(buttonSubmit) + suffix
onCancel: (e) =>
if @forceReload
@windowReload(e)

View File

@@ -78,7 +78,7 @@ class App.WidgetTextTools extends App.Controller
@contextData: (ce) ->
$(ce.element).data().plugin_texttools?.searchCondition or {}
@showModal: (e, ce, selection, el) ->
@showModal: (e, ce, selection, el, callback) ->
textToolId = $(e.target).data('id')
new App.TextToolsModal(
@@ -86,10 +86,12 @@ class App.WidgetTextTools extends App.Controller
contextData: @contextData(ce)
textTool: App.AITextTool.find(textToolId)
selectedText: selection.content
approve: (result) -> ce.replaceSelection(selection.ranges, result)
approve: (result) ->
ce.replaceSelection(selection.ranges, result)
callback() if callback
)
@renderDropdown: (e, ce, selection, el) ->
@renderDropdown: (e, ce, selection, el, callback) ->
bubbleMenuElement = $(e.target).closest('.js-bubbleMenu')
popupContainerElement = bubbleMenuElement.find('.dropup-container')
@@ -107,7 +109,7 @@ class App.WidgetTextTools extends App.Controller
textToolsDropdown.off('mousedown.text-tools-dropdown').on 'mousedown.text-tools-dropdown', '.js-action', (e) ->
e.preventDefault()
App.WidgetTextTools.showModal(e, ce, selection, el)
App.WidgetTextTools.showModal(e, ce, selection, el, callback)
textToolsDropdown.removeClass('open')
popupContainerElement.parent().removeClass('show-dropdown')

View File

@@ -5,5 +5,5 @@ App.Config.set('anthropic', {
prio: 4000
fields: ['token', 'model', 'ocr_active', 'ocr_model']
required: ['token']
default_model: 'claude-3-7-sonnet-latest'
default_model: 'claude-sonnet-4-6'
}, 'AIProviders')

View File

@@ -1,6 +1,6 @@
App.Config.set('azure', {
key: 'azure'
label: __('Azure AI')
label: __('Azure AI (legacy deployment-based endpoints)')
prio: 5000
fields: ['token', 'url_completions', 'ocr_active', 'url_ocr'] # TODO: Add url_embeddings when needed.
required: ['token', 'url_completions']

View File

@@ -5,5 +5,5 @@ App.Config.set('mistral', {
prio: 6000
fields: ['token', 'model', 'ocr_active', 'ocr_model']
required: ['token']
default_model: 'mistral-medium-latest'
default_model: 'mistral-large-2512'
}, 'AIProviders')

View File

@@ -6,5 +6,5 @@ App.Config.set('ollama', {
url_placeholder: 'http://localhost:11434'
fields: ['url', 'model', 'ocr_active', 'ocr_model']
required: ['url']
default_model: 'llama3.2'
default_model: 'mistral-small3.2'
}, 'AIProviders')

View File

@@ -17,7 +17,7 @@ class App.Auth
params.success(data, status, xhr)
error: (xhr, statusText, error) =>
@_loginError()
@_loginError(xhr)
params.error(xhr, statusText, error)
)
@@ -40,7 +40,7 @@ class App.Auth
callback()
error: (xhr, statusText, error) =>
@_loginError()
@_loginError(xhr)
)
@logout: (rerender = true, callback) ->
@@ -71,7 +71,7 @@ class App.Auth
@_logout(rerender, callback)
error: (xhr, statusText, error) =>
@_loginError()
@_loginError(xhr)
)
Spine.Ajax.queue(performLogut)
@@ -195,7 +195,10 @@ class App.Auth
if callback
callback()
@_loginError: ->
@_loginError: (data) ->
details = data.responseJSON || {}
App.Event.trigger('maintenance', type: 'invalid_csrf_token') if details.invalid_csrf_token
App.Log.debug 'Auth', '_loginError:error'
# empty session

View File

@@ -0,0 +1,30 @@
class App.SignatureHelper
# Look up active signature for a group. Returns { signature, group } or null.
@findForGroup: (groupId) ->
return null unless groupId
group = App.Group.find(groupId)
return null unless group?.signature_id
signature = App.Signature.find(group.signature_id)
return null unless signature?.active && signature.body
{ signature, group }
# Render signature body with tag replacement. Ticket data is passed in, session/config are resolved internally.
@render: (signatureBody, ticket) ->
App.Utils.replaceTags(signatureBody, { user: App.Session.get(), ticket: ticket, config: App.Config.all() })
# Build signature DOM element with data attributes (returns jQuery object).
@buildElement: (signatureId, renderedBody) ->
el = $("<div data-signature=\"true\" data-signature-id=\"#{signatureId}\">#{renderedBody}</div>")
App.Utils.htmlStrip(el)
el
# Remove top-level signatures from body (preserves signatures inside blockquotes).
@removeTopLevel: (body) ->
body.find('[data-signature=true]').not('blockquote [data-signature=true]').remove()
# Append signature at the bottom of body, with spacing if needed.
@appendToBottom: (body, signatureEl) ->
if !App.Utils.htmlLastLineEmpty(body)
body.append('<br><br>')
body.append(signatureEl)

View File

@@ -553,27 +553,6 @@ class App.Utils
if origHref != fixedHref then $(@).attr('href', fixedHref)
)
# signatureNeeded = App.Utils.signatureCheck(message, signature)
@signatureCheck: (message, signature) ->
messageText = message.replace(/<[^>]+>/g, ' ')
messageText = $('<div>' + messageText + '</div>').text().trim()
# New UI editor treats new lines differently.
# Thus the only way to match signature between new & old is to remove any white space characters.
messageText = messageText.replace(/\s+/g, ' ')
signatureText = signature.replace(/<[^>]+>/g, ' ')
signatureText = $('<div>' + signatureText + '</div>').text().trim()
signatureText = signatureText.replace(/\s+/g, ' ')
quote = (str) ->
(str + '').replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&")
#console.log('SC', messageText, signatureText, quote(signatureText))
regex = new RegExp(quote(signatureText), 'mi')
if messageText.match(regex)
false
else
true
# messageWithMarker = App.Utils.signatureIdentifyByPlaintext(message, false)
@signatureIdentifyByPlaintext: (message, test = false, internal = false) ->
textToSearch = @html2text(message)
@@ -1697,4 +1676,4 @@ class App.Utils
return
filename = @resolveFilename(xhr) || option.fallbackFilename
@downloadBlob(blob, filename)
@downloadBlob(blob, filename)

View File

@@ -319,28 +319,27 @@ class _webSocketSingleton extends App.Controller
)
_ajaxReceive: =>
return if !@client_id
return if @_ajaxReceiveWorking is true
@_ajaxReceiveWorking = true
App.Ajax.request(
id: 'message_receive',
type: 'POST'
url: @Config.get('api_path') + '/message_receive'
data: JSON.stringify(client_id: @client_id)
processData: false
success: (data) =>
@log 'debug', 'ajax:onmessage', data
@_receiveMessage(data)
if data && data.error
App.Delay.set(=>
return if !@client_id
App.Ajax.request(
id: 'message_receive',
type: 'POST'
url: @Config.get('api_path') + '/message_receive'
data: JSON.stringify(client_id: @client_id)
processData: false
success: (data) =>
@log 'debug', 'ajax:onmessage', data
@_receiveMessage(data)
if data && data.error
@client_id = undefined
@_ajaxInit(force: true)
@_ajaxReceive()
error: (data) =>
@client_id = undefined
@_ajaxInit(force: true)
@_ajaxReceiveWorking = false
@_ajaxReceive()
error: (data) =>
@client_id = undefined
@_ajaxInit(force: true)
@_ajaxReceiveWorking = false
)
)
, 2000, '_ajaxReceive', 'ws')
class Modal extends App.ControllerModal
buttonClose: false

View File

@@ -133,7 +133,7 @@ class App.SearchableAjaxSelect extends App.SearchableSelect
{
name: name
value: object.id
inactive: object.active == false
inactive: !@options.attribute.allowInactive && object.active == false
}
showLoader: =>

View File

@@ -38,6 +38,7 @@ class App.ExternalDataSourceAjaxSelect extends App.SearchableAjaxSelect
@attribute.valueRaw = JSON.stringify(@attribute.value)
@renderElement()
@toggleClear()
renderOptions: (options) ->
# We need to transform the value that we don't lose the needed type information.
@@ -130,6 +131,7 @@ class App.ExternalDataSourceAjaxSelect extends App.SearchableAjaxSelect
return
@setShadowValue(value: key, label: value)
@toggleClear()
onKeyUp: =>
return if @input.val().trim() isnt '' || @attribute.multiple

View File

@@ -675,7 +675,8 @@
'index': 'KnowledgeBase::Answer::Translation',
'url_type': 'agent',
'highlight_enabled': false,
'include_locale': true,
'include_locale': true,
'include_subtitle': true,
}),
processData: true,
success: function(data, status, xhr) {

View File

@@ -43,7 +43,7 @@ App.RichtextBubbleMenu =
actionKey = actionNode.dataset.key
activeAction = _.find(@allActions(), (item) -> item.key is actionKey)
activeAction.action(e, ce, selection, el) if activeAction
activeAction.action(e, ce, selection, el, closeDropdown) if activeAction
return false
)
@@ -61,8 +61,8 @@ App.RichtextBubbleMenu =
setTimeout ->
$(window).off('click.bubble-menu, keyup.bubble-menu').on 'click.bubble-menu, keyup.bubble-menu', (e) ->
return if e.type is 'keyup' and e.key.startsWith('Shift')
return if e.target.closest('.bubble-menu') # keep the bubble menu open if click is on the menu itself
return if not window.getSelection().isCollapsed # keep the bubble menu open if there is still a selection
return if e.target.closest('.bubble-menu') # keep the bubble menu open if the click is on the menu itself
closeDropdown()
$(window).off('click.bubble-menu, keyup.bubble-menu')

View File

@@ -106,15 +106,10 @@ App.ViewHelpers =
# define mask helper
# mask an value like 'a***********yz'
M: (item, start = 1, end = 2) ->
return '' if !item
string = ''
end = item.length - end - 1
for n in [0..item.length-1]
if start <= n && end >= n
string += '*'
else
string += item[n]
string
if item
'**********'
else
''
# define translation helper
T: (item, args...) ->

View File

@@ -16,7 +16,9 @@ class App.Model extends Spine.Model
@constructor.className
displayName: ->
return @name || '-'
return App.i18n.translatePlain(@name) or '-' if @translate()
@name or '-'
# shows the icon representing the object itself (e. g. the organization icon in organization profile or ticket sidebar)
icon: (user) ->
@@ -112,12 +114,14 @@ class App.Model extends Spine.Model
# return no errors
return
# keep in sync with ChecksCoreWorkflow::EMPTY_VALUES
@_validate_is_empty: (value) ->
return true if value is ''
return true if value is null
return true if value is undefined
return true if _.isArray(value) is true && value.length is 0
return true if _.isArray(value) is true && value.length is 1 && value[0] is ''
return true if _.isObject(value) is true && _.isEmpty(value)
false
###

View File

@@ -3,7 +3,7 @@ class App.DataPrivacyTask extends App.Model
@extend Spine.Model.Ajax
@url: @apiPath + '/data_privacy_tasks'
@configure_attributes = [
{ name: 'deletable_id', display: __('User'), tag: 'autocompletion_ajax', relation: 'User', do_not_log: true },
{ name: 'deletable_id', display: __('User'), tag: 'autocompletion_ajax', relation: 'User', allow_inactive: true, do_not_log: true },
{ name: 'state', display: __('State'), tag: 'input', readonly: 1 },
{ name: 'created_by_id', display: __('Created by'), relation: 'User', readonly: 1 },
{ name: 'created_at', display: __('Created'), tag: 'datetime', readonly: 1 },

View File

@@ -4,8 +4,8 @@ class App.PublicLink extends App.Model
@url: @apiPath + '/public_links'
@configure_attributes = [
{ name: 'link', display: __('Link'), tag: 'input', type: 'text', limit: 500, 'null': false, placeholder: 'https://link' },
{ name: 'title', display: __('Title'), tag: 'input', type: 'text', limit: 200, 'null': false },
{ name: 'description', display: __('Description (shown as title tag for screen readers)'), tag: 'input', type: 'text', limit: 200, 'null': true },
{ name: 'title', display: __('Title'), tag: 'input', type: 'text', limit: 200, 'null': false, translate: true },
{ name: 'description', display: __('Description (shown as title tag for screen readers)'), tag: 'input', type: 'text', limit: 200, 'null': true, translate: true },
{ name: 'screen', display: __('Context'), tag: 'multiselect', options: { login: __('Login Screen'), signup: __('Signup Screen'), password_reset: __('Forgot Password Screen') }, default: ['login'], 'null': false, multiple: true, translate: true },
{ name: 'new_tab', display: __('Open in new tab'), tag: 'select', options: { true: __('yes'), false: __('no'), }, default: true, 'null': false, translate: true },
{ name: 'prio', display: __('Prio'), readonly: 1 },

View File

@@ -10,9 +10,9 @@ class App.Webhook extends App.Model
{ name: 'ssl_verify', display: __('SSL verification'), tag: 'boolean', null: true, translate: true, options: { true: 'yes', false: 'no' }, default: true },
{ name: 'auth_type', display: __('Authentication'), tag: 'select', null: true, translate: true, nulloption: true, options: { 'basic_auth': __('HTTP Basic Authentication'), 'bearer_token': __('Bearer Token') } },
{ name: 'basic_auth_username', display: __('Username'), tag: 'input', type: 'text', limit: 250, null: true, item_class: 'formGroup--halfSize' },
{ name: 'basic_auth_password', display: __('Password'), tag: 'input', type: 'text', limit: 250, null: true, item_class: 'formGroup--halfSize' },
{ name: 'basic_auth_password', display: __('Password'), tag: 'input', type: 'text', input_type: 'password', limit: 250, null: true, item_class: 'formGroup--halfSize' },
{ name: 'bearer_token', display: __('Bearer Token'), tag: 'input', type: 'text', input_type: 'password', limit: 250, null: true },
{ name: 'signature_token', display: __('HMAC SHA1 Signature Token'), tag: 'input', type: 'text', limit: 100, null: true },
{ name: 'signature_token', display: __('HMAC SHA1 Signature Token'), tag: 'input', type: 'text', input_type: 'password', limit: 200, null: true },
{ name: 'customized_payload', display: __('Custom Payload'), tag: 'switch', null: true, label_class: 'hidden' },
{ name: 'custom_payload', display: __('Custom Payload'), tag: 'code_editor', null: true, collapsible: true, label_class: 'hidden', hint: __('To revert back to the default payload, simply turn off the "Custom Payload" switch above.') },
{ name: 'note', display: __('Note'), tag: 'richtext', null: true, note: '', limit: 250 },
@@ -36,4 +36,4 @@ You can use webhooks in Zammad to send ticket, article, and attachment data when
return @name if !@endpoint
if @active is false
return "#{@name} (#{@endpoint}) (#{App.i18n.translateInline('inactive')})"
"#{@name} (#{@endpoint})"
"#{@name} (#{@endpoint})"

View File

@@ -1,3 +1,3 @@
<div class="js-missingProviderAlert alert alert--warning <% if !@visible: %>hide<% end %>">
<%- @T('The provider configuration is missing. Please set up the provider before proceeding in |AI > Providers|.') %>
<%- @T('The provider configuration is disabled. Please set up the provider before proceeding in |AI > Providers|.') %>
</div>

View File

@@ -215,7 +215,7 @@ $(function() {
<li><%- @T('The chat is turned off.') %>
<li><%- @T('There are too many people in the chat queue.') %>
</ol>
<%- @T('When you turn on debugging by setting the option §debug§ to §true§ the reason gets printed to the javascript console.') %>
<%- @T('When you turn on debugging by setting the option §debug§ to §true§ the reason gets printed to the JavaScript console.') %>
</p>
<hr>

View File

@@ -148,7 +148,7 @@
<p><%- @T("Zammad Forms requires jQuery. If you don't already use it on your website, you can add it like this:") %></p>
<pre><code class="language-html js-code">&lt;script src="https://code.jquery.com/jquery-3.6.0.min.js"&gt;&lt;/script&gt;</code></pre>
<p><%- @T('You need to add the following Javascript code snippet to your web page:') %></p>
<p><%- @T('You need to add the following JavaScript code snippet to your web page:') %></p>
<pre class="js-modal"><code class="language-html js-code js-paramsBlock">&lt;button id="zammad-feedback-form"&gt;Feedback&lt;/button&gt;

View File

@@ -17,7 +17,7 @@
<label for="application_secret"><%- @T('Facebook App Secret') %> <span>*</span></label>
</div>
<div class="controls">
<input id="application_secret" type="text" name="application_secret" value="<% if @external_credential && @external_credential.credentials: %><%= @external_credential.credentials.application_secret %><% end %>" class="form-control" required autocomplete="off">
<input id="application_secret" type="password" name="application_secret" value="<% if @external_credential && @external_credential.credentials: %><%= @external_credential.credentials.application_secret %><% end %>" class="form-control" required autocomplete="off">
</div>
</div>
<h2><%- @T('Your callback URL') %></h2>

View File

@@ -18,9 +18,10 @@
<% for id, day of @days: %>
<% if @hours[id].timeframes[slot]: %>
<td data-day="<%- id %>" data-slot="<%- slot %>" class="form-group day-time<%- ' is-active' if @hours[id].active %>">
<label for="<%- id %>_start_time"><%- @T('From') %></label>
<label for="<%- id %>_start_time" class="sr-only"><%- @T('Start time') %></label>
<input type="text" id="<%- id %>_start_time" name="<%= @attribute.name %>::<%- id %>::start" value="<%- @hours[id].timeframes[slot][0] %>" data-day="<%- id %>" data-slot="<%- slot %>" data-i="0" class="form-control form-control--small time js-time">
<label for="<%- id %>_end_time"><%- @T('Till') %></label>
<div class="day-time-separator">&ndash;</div>
<label for="<%- id %>_end_time" class="sr-only"><%- @T('End time') %></label>
<input type="text" id="<%- id %>_end_time" name="<%= @attribute.name %>::<%- id %>::end" value="<%- @hours[id].timeframes[slot][1] %>" data-day="<%- id %>" data-slot="<%- slot %>" data-i="1" class="form-control form-control--small time js-time">
<% else: %>
<td class="empty-cell">

View File

@@ -8,11 +8,11 @@
<% end %>
<div class="event-row">
<div>
<h4><%- @T('From') %></h4>
<h4><%- @T('Start date') %></h4>
<p><%- @datetime(event.start_date) %></p>
</div>
<div>
<h4><%- @T('Till') %></h4>
<h4><%- @T('End date') %></h4>
<p><%- @datetime(event.end_date) %></p>
</div>
</div>

View File

@@ -1,4 +1,4 @@
<div class="<%= @attribute.class %> horizontal-filters-switch horizontal-filters-switch--align-start">
<div class="<%= @attribute.class %> horizontal-filters-switch horizontal-filters-switch--align-start"<% if @attribute.theme: %> data-theme="<%= @attribute.theme %>"<% end %>>
<label>
<%- @T(@attribute.display) %>
<div class="zammad-switch zammad-switch--small js-switch">

View File

@@ -5,7 +5,7 @@
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
<span class="label-text"><%- @Ti(role.displayName()) %></span>
<% if role.note: %><span class="help-text"><%- @T(role.note) %></span><% end %>
<% if role.note: %><span class="help-text"><%- @T(App.Utils.html2text(role.note)) %></span><% end %>
</label>
<% end %>
</div>

Some files were not shown because too many files have changed in this diff Show More