Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8777ff5c85 | ||
|
|
bf1e58804f | ||
|
|
4e75f8ff35 | ||
|
|
dad0af4b86 | ||
|
|
692addcf2a | ||
|
|
2bfa144ce0 | ||
|
|
67889e5970 | ||
|
|
215bbe7884 | ||
|
|
bcdf306a75 | ||
|
|
f82ce99ca6 | ||
|
|
a5f314949e | ||
|
|
7ce3166cef | ||
|
|
a4ded74601 | ||
|
|
8212295b27 | ||
|
|
fd84f3a102 | ||
|
|
63c23f22de | ||
|
|
992e734c9e | ||
|
|
375c492931 | ||
|
|
19bb0240db | ||
|
|
528479cee6 | ||
|
|
40972b3fde | ||
|
|
d131f6c5d3 | ||
|
|
d79c85cfb6 | ||
|
|
02066c1a24 | ||
|
|
19b597132b | ||
|
|
566eab44f7 | ||
|
|
de2e8f4b8e | ||
|
|
16fc27bdd6 | ||
|
|
e0ff35cb14 |
@@ -423,7 +423,7 @@ test:browser:integration:api_client_php:
|
||||
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD: "test"
|
||||
script:
|
||||
- RAILS_ENV=test bundle exec rake db:create
|
||||
- bundle exec rake zammad:ci:test:start zammad:setup:auto_wizard
|
||||
- RAILS_ENV=test bundle exec rake zammad:ci:test:start zammad:setup:auto_wizard
|
||||
- git clone https://github.com/zammad/zammad-api-client-php.git
|
||||
- php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
- php composer-setup.php --install-dir=/usr/local/bin
|
||||
|
||||
13
.pkgr.yml
13
.pkgr.yml
@@ -16,6 +16,19 @@ targets:
|
||||
build_dependencies:
|
||||
- http://download.fedoraproject.org/pub/epel/7/x86_64/Packages/i/imlib2-1.4.5-9.el7.x86_64.rpm
|
||||
- http://download.fedoraproject.org/pub/epel/7/x86_64/Packages/i/imlib2-devel-1.4.5-9.el7.x86_64.rpm
|
||||
centos-8:
|
||||
dependencies:
|
||||
- curl
|
||||
- elasticsearch
|
||||
- nginx
|
||||
- postgresql-server
|
||||
- which
|
||||
- epel-release
|
||||
- imlib2
|
||||
- imlib2-devel
|
||||
build_dependencies:
|
||||
- http://download.fedoraproject.org/pub/epel/8/Everything/x86_64/Packages/i/imlib2-1.4.9-8.el8.x86_64.rpm
|
||||
- http://download.fedoraproject.org/pub/epel/8/Everything/x86_64/Packages/i/imlib2-devel-1.4.9-8.el8.x86_64.rpm
|
||||
debian-8:
|
||||
dependencies:
|
||||
- curl
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
# Configuration parameters: CountComments, ExcludedMethods.
|
||||
# ExcludedMethods: refine
|
||||
Metrics/BlockLength:
|
||||
Max: 1987
|
||||
Max: 1653
|
||||
Exclude:
|
||||
- 'spec/requests/ticket_spec.rb'
|
||||
|
||||
# Offense count: 16
|
||||
RSpec/AnyInstance:
|
||||
|
||||
88
CHANGELOG.md
88
CHANGELOG.md
@@ -1,15 +1,93 @@
|
||||
# Change Log
|
||||
|
||||
## [3.2.0](https://github.com/zammad/zammad/tree/3.2.0) (2019-xx-xx)
|
||||
[Full Changelog](https://github.com/zammad/zammad/compare/3.1.0...3.2.0)
|
||||
## [3.2.1](https://github.com/zammad/zammad/tree/3.2.1) (2020-03-03)
|
||||
[Full Changelog](https://github.com/zammad/zammad/compare/3.2.0...3.2.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
**Fixed bugs:**
|
||||
- Moving from Manage Knowledge base view to another Manage view requires multiple clicks [2895](https://github.com/zammad/zammad/issues/2895) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Forwarding: Attachment of original content gets removed starting from the second forward without reloading per ticket [2406](https://github.com/zammad/zammad/issues/2406) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||
- Tweet which is containing * will not send out, Twitter::Error::Unauthorized: Could not authenticate you. [2873](https://github.com/zammad/zammad/issues/2873) [[third party issue](https://github.com/zammad/zammad/labels/third party issue)]
|
||||
- Added Swedish chat translation [2869](https://github.com/zammad/zammad/pull/2869)
|
||||
- Error while fetching email from inbox will block processing of other mails in inbox [2754](https://github.com/zammad/zammad/issues/2754) [[bug](https://github.com/zammad/zammad/labels/bug)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||
- Invalid stored Twitter credentials block migration [2826](https://github.com/zammad/zammad/issues/2826) [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)] [[migration](https://github.com/zammad/zammad/labels/migration)]
|
||||
- Zammad doesn't work with Elasticsearch 7.5.x [2824](https://github.com/zammad/zammad/issues/2824) [[bug](https://github.com/zammad/zammad/labels/bug)] [[search](https://github.com/zammad/zammad/labels/search)]
|
||||
- Unable to open notifications (if chat is enabled) [2823](https://github.com/zammad/zammad/issues/2823) [[bug](https://github.com/zammad/zammad/labels/bug)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||
|
||||
|
||||
## [3.2.0](https://github.com/zammad/zammad/tree/3.2.0) (2019-12-02)
|
||||
[Full Changelog](https://github.com/zammad/zammad/compare/3.1.0...3.2.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
- multiselect for field type select in trigger filters [1559](https://github.com/zammad/zammad/issues/1559) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[trigger](https://github.com/zammad/zammad/labels/trigger)]
|
||||
- Chat enhancement: Option to set Agent display name [2291](https://github.com/zammad/zammad/issues/2291) [[chat](https://github.com/zammad/zammad/labels/chat)] [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Article deletion for agents within 10 minutes of creation [2687](https://github.com/zammad/zammad/issues/2687) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||
- Allow direct or extention numbers in answeringNumber of CTI API [2797](https://github.com/zammad/zammad/issues/2797) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Backup permission issue: Could not change directory [2508](https://github.com/zammad/zammad/issues/2508) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Triggers not present in the permissions list of a role [2608](https://github.com/zammad/zammad/issues/2608) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Telegram: Can't receive Videos and "too large" files [2289](https://github.com/zammad/zammad/issues/2289) [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)] [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[telegram](https://github.com/zammad/zammad/labels/telegram)]
|
||||
- Send postmaster email to sender if email is too big and not processed by Zammad [2092](https://github.com/zammad/zammad/issues/2092) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Enhance postmaster filters to be able ignoring vacation mails (out of office) [2734](https://github.com/zammad/zammad/issues/2734) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Display only used Channels in Dashboard [1010](https://github.com/zammad/zammad/issues/1010) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Sometimes elasticsearch takes longer as expected and will raise timeout in Zammad [2720](https://github.com/zammad/zammad/issues/2720) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Enhance CTI-Caller-Log to "mark as seen" all unread entries at once [2647](https://github.com/zammad/zammad/issues/2647) [[CTI](https://github.com/zammad/zammad/labels/CTI)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Added new placetel outbound support for call logging. [2703](https://github.com/zammad/zammad/issues/2703) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- No description for Macros [813](https://github.com/zammad/zammad/issues/813) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||
- Trigger word "attachment" leads to attachment warning pop-up even if it is just in quoted customer text. [2325](https://github.com/zammad/zammad/issues/2325) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
|
||||
|
||||
- Insertion of inline pictures does not work, if you copy an image in your Browser [2645](https://github.com/zammad/zammad/issues/2645) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||
- It seems you're running an external Elasticsearch server on http://localhost:9200 [2400](https://github.com/zammad/zammad/issues/2400) [[bug](https://github.com/zammad/zammad/labels/bug)] [[update](https://github.com/zammad/zammad/labels/update)]
|
||||
- ticket auto assignment is not working if only one excluded user is defined [2692](https://github.com/zammad/zammad/issues/2692) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Can't add new articles via Macro [542](https://github.com/zammad/zammad/issues/542) [[bug](https://github.com/zammad/zammad/labels/bug)] [[macros](https://github.com/zammad/zammad/labels/macros)]
|
||||
- Error while fetching email from inbox will block processing of other mails in inbox [2754](https://github.com/zammad/zammad/issues/2754) [[bug](https://github.com/zammad/zammad/labels/bug)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||
- Useless display of delete icon below note [2813](https://github.com/zammad/zammad/issues/2813) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||
- TimeAccounting modal dialog input not focused [2818](https://github.com/zammad/zammad/issues/2818) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[regression](https://github.com/zammad/zammad/labels/regression)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||
- Datepicker in bulk action not usable in bulk action if ticket list is longer the one page. [2274](https://github.com/zammad/zammad/issues/2274) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)] [[overviews](https://github.com/zammad/zammad/labels/overviews)]
|
||||
- Array values (e.g. several states) nuke reporting profiles [2809](https://github.com/zammad/zammad/issues/2809) [[blocker](https://github.com/zammad/zammad/labels/blocker)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[regression](https://github.com/zammad/zammad/labels/regression)] [[reporting](https://github.com/zammad/zammad/labels/reporting)]
|
||||
- Unprocessable mail if follow up search is enabled for attachments with contains invalid charsets (postmaster_follow_up_search_in with attachment) [2808](https://github.com/zammad/zammad/issues/2808) [[bug](https://github.com/zammad/zammad/labels/bug)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||
- Admin Settings-> Knowledge base messes up display of other Admin settings [2784](https://github.com/zammad/zammad/issues/2784) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Zammad ignores disabled Chat-Topics for agents [2441](https://github.com/zammad/zammad/issues/2441) [[bug](https://github.com/zammad/zammad/labels/bug)] [[chat](https://github.com/zammad/zammad/labels/chat)]
|
||||
- Knowledge Base - Attachments not working for internal readers [2777](https://github.com/zammad/zammad/issues/2777) [[bug](https://github.com/zammad/zammad/labels/bug)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||
- Twitter DMs and Mentions do not arrive [2460](https://github.com/zammad/zammad/issues/2460) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||
- Image/attachment preview is to small, need to download image and open it locally [2796](https://github.com/zammad/zammad/issues/2796) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||
- Reporting does not return all tickets when using values with that "devide" values (e. g. some_value) [2785](https://github.com/zammad/zammad/issues/2785) [[bug](https://github.com/zammad/zammad/labels/bug)] [[reporting](https://github.com/zammad/zammad/labels/reporting)]
|
||||
- not possible to search or link KB-answer within ticket [2789](https://github.com/zammad/zammad/issues/2789) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||
- Wrong dates on synced calendars [2229](https://github.com/zammad/zammad/issues/2229) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||
- CSV-Import doesn't lookup records by "name" field [2658](https://github.com/zammad/zammad/issues/2658) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||
- Signature detection fails if no signature range could be found [2740](https://github.com/zammad/zammad/issues/2740) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Can't forward articles containing a <img> tag without src attribute [2701](https://github.com/zammad/zammad/issues/2701) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||
- Unable to process email where group has option `follow_up_possible: 'new_ticket'` and arraiving email has no subject header [2768](https://github.com/zammad/zammad/issues/2768) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Removing a record drops whole index on Elasticsearch 6 and later [2742](https://github.com/zammad/zammad/issues/2742) [[bug](https://github.com/zammad/zammad/labels/bug)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||
- Can't change Ticket attributes of "Shared Organization" Ticket [2750](https://github.com/zammad/zammad/issues/2750) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- SAML authentication [2741](https://github.com/zammad/zammad/issues/2741) [[authentication](https://github.com/zammad/zammad/labels/authentication)]
|
||||
- Fixes #2699 - Excel export of report or time accounting drops additional fields in row if integer field contain `null/nil/undefined`. [2699](https://github.com/zammad/zammad/issues/2699) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- System mail notification subjects ending with =0A [2726](https://github.com/zammad/zammad/issues/2726) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Default value not set for attributes of type input, select, tree_select, richtext, textarea, checkbox [1653](https://github.com/zammad/zammad/issues/1653) [[bug](https://github.com/zammad/zammad/labels/bug)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
||||
- Gitlab Login not working after upgrade to Zammad 3.x [2595](https://github.com/zammad/zammad/issues/2595) [[authentication](https://github.com/zammad/zammad/labels/authentication)] [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Registering of Twitter App fails for Twitter App with existing webhooks [2730](https://github.com/zammad/zammad/issues/2730) [[admin area](https://github.com/zammad/zammad/labels/admin area)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)]
|
||||
- No owner set for Tickets imported from Zendesk [2702](https://github.com/zammad/zammad/issues/2702) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||
- Zendesk import doesn't consider all tickets [2694](https://github.com/zammad/zammad/issues/2694) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||
- Zendesk import fails for deleted tickets [1161](https://github.com/zammad/zammad/issues/1161) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||
- Zendesk import fails due import of (too long) ticket description as (unused) note [2538](https://github.com/zammad/zammad/issues/2538) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||
- Put _type, _doc and _id to reserved words for object managaer becasue of elasticssearch internal use [2728](https://github.com/zammad/zammad/issues/2728) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Quick text launched in text editor even if new line is between :: [2710](https://github.com/zammad/zammad/issues/2710) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||
- Unable to process mail with enabled follow up detection in content of attachments [2723](https://github.com/zammad/zammad/issues/2723) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- mail processing impossible: invalid byte sequence in UTF-8 [2722](https://github.com/zammad/zammad/issues/2722) [[bug](https://github.com/zammad/zammad/labels/bug)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||
- Unable to set owner via `X-Zammad-Ticket-owner` header [2719](https://github.com/zammad/zammad/issues/2719) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Chat header's height is too small [2596](https://github.com/zammad/zammad/issues/2596) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[chat](https://github.com/zammad/zammad/labels/chat)]
|
||||
- Inconsistency in "follow up" strings [2661](https://github.com/zammad/zammad/issues/2661) [[bug](https://github.com/zammad/zammad/labels/bug)] [[translation](https://github.com/zammad/zammad/labels/translation)]
|
||||
- Limit on copy and paste in note box for new articles [2696](https://github.com/zammad/zammad/issues/2696) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||
- Telegram not possible to close ticket via `/end` [2695](https://github.com/zammad/zammad/issues/2695) [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)] [[telegram](https://github.com/zammad/zammad/labels/telegram)]
|
||||
- clickable URL (template) for ObjectManager Attribute [2103](https://github.com/zammad/zammad/issues/2103) [[feature backlog](https://github.com/zammad/zammad/labels/feature backlog)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
||||
- Time zone incorrectly calculated in ticket list [2697](https://github.com/zammad/zammad/issues/2697) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- ObjectManager::Attribute names collide with reserved application words ( e.g. `url`) and break functionalities [2664](https://github.com/zammad/zammad/issues/2664) [[bug](https://github.com/zammad/zammad/labels/bug)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
||||
- Zammad ignores group mapping on Telegram if same sending user [2379](https://github.com/zammad/zammad/issues/2379) [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)]
|
||||
- Linking of external account in profile is broken [2675](https://github.com/zammad/zammad/issues/2675) [[authentication](https://github.com/zammad/zammad/labels/authentication)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[personal settings/menu](https://github.com/zammad/zammad/labels/personal settings/menu)]
|
||||
- Scheduler/Job with current_user in condition is not working [2685](https://github.com/zammad/zammad/issues/2685) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Issue2541FixNotificationEmailWithoutBody database migration blocks update when records with `current_user.id` in selector exist [2663](https://github.com/zammad/zammad/issues/2663) [[bug](https://github.com/zammad/zammad/labels/bug)] [[update](https://github.com/zammad/zammad/labels/update)]
|
||||
- Zammad logs permanently to STDOUT on 3.1 [2660](https://github.com/zammad/zammad/issues/2660) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||
- Last line in note is not selectable [2654](https://github.com/zammad/zammad/issues/2654) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||
- Services die with: ThreadError: can't create Thread: Resource temporarily unavailable [2656](https://github.com/zammad/zammad/issues/2656) [[bug](https://github.com/zammad/zammad/labels/bug)] [[third party issue](https://github.com/zammad/zammad/labels/third party issue)] [[waiting for feedback](https://github.com/zammad/zammad/labels/waiting for feedback)]
|
||||
|
||||
|
||||
6
Gemfile
6
Gemfile
@@ -2,7 +2,7 @@ source 'https://rubygems.org'
|
||||
|
||||
# core - base
|
||||
ruby '2.5.5'
|
||||
gem 'rails', '5.2.3'
|
||||
gem 'rails', '5.2.4.1'
|
||||
|
||||
# core - rails additions
|
||||
gem 'activerecord-import'
|
||||
@@ -13,7 +13,7 @@ gem 'json'
|
||||
gem 'rails-observers'
|
||||
|
||||
# core - application servers
|
||||
gem 'puma', group: :puma
|
||||
gem 'puma', '~>3.12', group: :puma
|
||||
gem 'unicorn', group: :unicorn
|
||||
|
||||
# core - supported ORMs
|
||||
@@ -49,7 +49,7 @@ gem 'eco'
|
||||
gem 'sassc-rails'
|
||||
|
||||
# asset handling - pipeline
|
||||
gem 'sprockets'
|
||||
gem 'sprockets', '~> 3.7.2'
|
||||
gem 'uglifier'
|
||||
|
||||
gem 'autoprefixer-rails'
|
||||
|
||||
108
Gemfile.lock
108
Gemfile.lock
@@ -49,37 +49,37 @@ GEM
|
||||
specs:
|
||||
aasm (5.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
actioncable (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
actioncable (5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailer (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activejob (= 5.2.3)
|
||||
actionmailer (5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
actionview (= 5.2.4.1)
|
||||
activejob (= 5.2.4.1)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
rack (~> 2.0)
|
||||
actionpack (5.2.4.1)
|
||||
actionview (= 5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
rack (~> 2.0, >= 2.0.8)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
actionview (5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
activejob (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
activejob (5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
activerecord (5.2.3)
|
||||
activemodel (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
activemodel (5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
activerecord (5.2.4.1)
|
||||
activemodel (= 5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
arel (>= 9.0)
|
||||
activerecord-import (1.0.2)
|
||||
activerecord (>= 3.2)
|
||||
@@ -91,11 +91,11 @@ GEM
|
||||
multi_json (~> 1.11, >= 1.11.2)
|
||||
rack (>= 1.5.2, < 3)
|
||||
railties (>= 4.0)
|
||||
activestorage (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
activerecord (= 5.2.3)
|
||||
activestorage (5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
activerecord (= 5.2.4.1)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (5.2.3)
|
||||
activesupport (5.2.4.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
@@ -120,7 +120,7 @@ GEM
|
||||
msgpack (~> 1.0)
|
||||
browser (2.5.3)
|
||||
buftok (0.2.0)
|
||||
builder (3.2.3)
|
||||
builder (3.2.4)
|
||||
byebug (11.0.1)
|
||||
capybara (3.21.0)
|
||||
addressable
|
||||
@@ -185,7 +185,7 @@ GEM
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
equalizer (0.0.11)
|
||||
erubi (1.8.0)
|
||||
erubi (1.9.0)
|
||||
eventmachine (1.2.7)
|
||||
execjs (2.7.0)
|
||||
factory_bot (5.0.2)
|
||||
@@ -245,7 +245,7 @@ GEM
|
||||
http-form_data (2.1.1)
|
||||
http_parser.rb (0.6.0)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.6.0)
|
||||
i18n (1.7.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
icalendar (2.5.3)
|
||||
ice_cube (~> 0.16)
|
||||
@@ -272,7 +272,7 @@ GEM
|
||||
logging (2.2.2)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.10)
|
||||
loofah (2.3.1)
|
||||
loofah (2.4.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
lumberjack (1.0.13)
|
||||
@@ -285,11 +285,11 @@ GEM
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2019.0331)
|
||||
mimemagic (0.3.3)
|
||||
mini_mime (1.0.1)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
mini_racer (0.2.4)
|
||||
libv8 (>= 6.3)
|
||||
minitest (5.11.3)
|
||||
minitest (5.13.0)
|
||||
msgpack (1.2.4)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
@@ -300,8 +300,8 @@ GEM
|
||||
nestful (1.1.3)
|
||||
net-ldap (0.16.1)
|
||||
netrc (0.11.0)
|
||||
nio4r (2.3.1)
|
||||
nokogiri (1.10.5)
|
||||
nio4r (2.5.2)
|
||||
nokogiri (1.10.8)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
nori (2.6.0)
|
||||
notiffany (0.1.1)
|
||||
@@ -377,24 +377,24 @@ GEM
|
||||
binding_of_caller (>= 0.7)
|
||||
pry (>= 0.9.11)
|
||||
public_suffix (3.0.3)
|
||||
puma (3.12.1)
|
||||
rack (2.0.7)
|
||||
puma (3.12.4)
|
||||
rack (2.0.8)
|
||||
rack-livereload (0.3.17)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.3)
|
||||
actioncable (= 5.2.3)
|
||||
actionmailer (= 5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activejob (= 5.2.3)
|
||||
activemodel (= 5.2.3)
|
||||
activerecord (= 5.2.3)
|
||||
activestorage (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
rails (5.2.4.1)
|
||||
actioncable (= 5.2.4.1)
|
||||
actionmailer (= 5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
actionview (= 5.2.4.1)
|
||||
activejob (= 5.2.4.1)
|
||||
activemodel (= 5.2.4.1)
|
||||
activerecord (= 5.2.4.1)
|
||||
activestorage (= 5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.3)
|
||||
railties (= 5.2.4.1)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.4)
|
||||
actionpack (>= 5.0.1.x)
|
||||
@@ -403,19 +403,19 @@ GEM
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.0.4)
|
||||
loofah (~> 2.2, >= 2.2.2)
|
||||
rails-html-sanitizer (1.3.0)
|
||||
loofah (~> 2.3)
|
||||
rails-observers (0.1.5)
|
||||
activemodel (>= 4.0)
|
||||
railties (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
railties (5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.0)
|
||||
rake (12.3.2)
|
||||
rake (12.3.3)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.10.0)
|
||||
ffi (~> 1.0)
|
||||
@@ -511,7 +511,7 @@ GEM
|
||||
tins (~> 1.0)
|
||||
test-unit (3.3.3)
|
||||
power_assert
|
||||
thor (0.20.3)
|
||||
thor (1.0.1)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.9)
|
||||
tins (1.20.2)
|
||||
@@ -624,9 +624,9 @@ DEPENDENCIES
|
||||
pry-remote
|
||||
pry-rescue
|
||||
pry-stack_explorer
|
||||
puma
|
||||
puma (~> 3.12)
|
||||
rack-livereload
|
||||
rails (= 5.2.3)
|
||||
rails (= 5.2.4.1)
|
||||
rails-controller-testing
|
||||
rails-observers
|
||||
rb-fsevent
|
||||
@@ -647,7 +647,7 @@ DEPENDENCIES
|
||||
spring
|
||||
spring-commands-rspec
|
||||
spring-commands-testunit
|
||||
sprockets
|
||||
sprockets (~> 3.7.2)
|
||||
tcr!
|
||||
telegramAPI
|
||||
telephone_number
|
||||
|
||||
@@ -51,7 +51,7 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
|
||||
|
||||
release: ->
|
||||
super
|
||||
@modal.el.remove()
|
||||
@modal?.el.remove()
|
||||
|
||||
processLoaded: ->
|
||||
if @knowledge_base_id
|
||||
|
||||
@@ -17,6 +17,7 @@ class App.CustomerChat extends App.Controller
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
@popovers = []
|
||||
@chatWindows = {}
|
||||
@maxChatWindows = 4
|
||||
preferences = @Session.get('preferences')
|
||||
@@ -221,7 +222,12 @@ class App.CustomerChat extends App.Controller
|
||||
chats
|
||||
|
||||
updateMeta: =>
|
||||
$('.popover').remove()
|
||||
|
||||
# clear old popovers
|
||||
for popover in @popovers
|
||||
popover.popover('destroy')
|
||||
@popovers = []
|
||||
|
||||
activeChatTopcis = @activeChatTopcis()
|
||||
@$('.js-header').html(App.view('customer_chat/chat_header')(chats: activeChatTopcis))
|
||||
@refreshElements()
|
||||
@@ -244,7 +250,7 @@ class App.CustomerChat extends App.Controller
|
||||
for chat in App.Chat.all()
|
||||
do (chat) =>
|
||||
@$(".js-header .js-waitingCustomers[data-chat-id=#{chat.id}] .js-badgeWaitingCustomers").text(@meta.waiting_chat_count_by_chat[chat.id])
|
||||
@el.find(".js-waitingCustomers[data-chat-id=#{chat.id}] .js-info").popover(
|
||||
@popovers.push @el.find(".js-waitingCustomers[data-chat-id=#{chat.id}] .js-info").popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
@@ -258,7 +264,7 @@ class App.CustomerChat extends App.Controller
|
||||
)
|
||||
else
|
||||
@badgeWaitingCustomers.text(@meta.waiting_chat_count)
|
||||
@el.find('.js-waitingCustomers .js-totalInfo').popover(
|
||||
@popovers.push @el.find('.js-waitingCustomers .js-totalInfo').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
@@ -280,7 +286,7 @@ class App.CustomerChat extends App.Controller
|
||||
@addChat(session)
|
||||
@meta.active_sessions = false
|
||||
|
||||
@el.find('.js-chattingCustomers .js-info').popover(
|
||||
@popovers.push @el.find('.js-chattingCustomers .js-info').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
@@ -293,7 +299,7 @@ class App.CustomerChat extends App.Controller
|
||||
@chatSessionList(@meta.running_chat_session_list)
|
||||
)
|
||||
|
||||
@el.find('.js-activeAgents .js-info').popover(
|
||||
@popovers.push @el.find('.js-activeAgents .js-info').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
|
||||
@@ -58,22 +58,13 @@ class Index extends App.ControllerContent
|
||||
)
|
||||
|
||||
success: (data) =>
|
||||
if data.message is 'ok'
|
||||
|
||||
# if in developer mode, redirect to set new password
|
||||
if data.token && @Config.get('developer_mode') is true
|
||||
redirect = =>
|
||||
@navigate "#password_reset_verify/#{data.token}"
|
||||
@delay(redirect, 2000)
|
||||
@render(sent: true)
|
||||
|
||||
else
|
||||
@$('[name=username]').val('')
|
||||
@notify(
|
||||
type: 'error'
|
||||
msg: App.i18n.translateContent('Username or email address invalid, please try again.')
|
||||
)
|
||||
@formEnable( @el.find('.form-password') )
|
||||
# if in developer mode, redirect to set new password
|
||||
if data.token && @Config.get('developer_mode') is true
|
||||
redirect = =>
|
||||
@navigate "#password_reset_verify/#{data.token}"
|
||||
@delay(redirect, 2000)
|
||||
@render(sent: true)
|
||||
|
||||
App.Config.set('password_reset', Index, 'Routes')
|
||||
|
||||
|
||||
@@ -1005,9 +1005,12 @@ class App.TicketZoom extends App.Controller
|
||||
# reset task state
|
||||
taskReset: =>
|
||||
@form_id = App.ControllerForm.formId()
|
||||
|
||||
@articleNew.form_id = @form_id
|
||||
@articleNew.render()
|
||||
|
||||
@articleView.updateFormId(@form_id)
|
||||
|
||||
@localTaskData =
|
||||
ticket: {}
|
||||
article: {}
|
||||
|
||||
@@ -132,7 +132,10 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||
textRange.select()
|
||||
|
||||
isIE10: ->
|
||||
Function('/*@cc_on return document.documentMode===10@*/')()
|
||||
detected = App.Browser.detection()
|
||||
return false if !detected.browser
|
||||
return false if detected.browser.name != 'Explorer'
|
||||
return detected.browser.major == 10
|
||||
|
||||
release: =>
|
||||
if @subscribeIdTextModule
|
||||
|
||||
@@ -60,6 +60,9 @@ class App.TicketZoomArticleView extends App.Controller
|
||||
|
||||
false
|
||||
|
||||
updateFormId: (newFormId) ->
|
||||
for id, viewItem of @articleController
|
||||
viewItem.updateFormId(newFormId)
|
||||
|
||||
class ArticleViewItem extends App.ObserverController
|
||||
model: 'TicketArticle'
|
||||
@@ -131,7 +134,12 @@ class ArticleViewItem extends App.ObserverController
|
||||
attachments = App.TicketArticle.contentAttachments(article)
|
||||
if article.attachments
|
||||
for attachment in article.attachments
|
||||
attachment.url = "#{App.Config.get('api_path')}/ticket_attachment/#{article.ticket_id}/#{article.id}/#{attachment.id}?disposition=attachment"
|
||||
|
||||
dispositionParams = ''
|
||||
if attachment?.preferences['Content-Type'] isnt 'application/pdf' && attachment?.preferences['Content-Type'] isnt 'text/html'
|
||||
dispositionParams = '?disposition=attachment'
|
||||
|
||||
attachment.url = "#{App.Config.get('api_path')}/ticket_attachment/#{article.ticket_id}/#{article.id}/#{attachment.id}#{dispositionParams}"
|
||||
attachment.preview_url = "#{App.Config.get('api_path')}/ticket_attachment/#{article.ticket_id}/#{article.id}/#{attachment.id}?view=preview"
|
||||
|
||||
if attachment && attachment.preferences && attachment.preferences['original-format'] is true
|
||||
@@ -205,7 +213,7 @@ class ArticleViewItem extends App.ObserverController
|
||||
size: 40
|
||||
)
|
||||
|
||||
new App.TicketZoomArticleActions(
|
||||
@articleActions = new App.TicketZoomArticleActions(
|
||||
el: @$('.js-article-actions')
|
||||
ticket: @ticket
|
||||
article: article
|
||||
@@ -425,3 +433,6 @@ class ArticleViewItem extends App.ObserverController
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
new App.TicketZoomArticleImageView(image: $(e.target).get(0).outerHTML, parentElement: $(e.currentTarget))
|
||||
|
||||
updateFormId: (newFormId) ->
|
||||
@articleActions?.form_id = newFormId
|
||||
|
||||
@@ -47,6 +47,33 @@ class Widget extends App.Controller
|
||||
# enable translation inline
|
||||
App.Config.set('translation_inline', true)
|
||||
|
||||
@observer = new MutationObserver((mutations) ->
|
||||
|
||||
mutations.forEach((mutation) ->
|
||||
|
||||
mutation.addedNodes.forEach((addedNode) ->
|
||||
|
||||
$(addedNode).find('span.translation').on('click.translation', (e) ->
|
||||
e.stopPropagation()
|
||||
return false
|
||||
)
|
||||
$(addedNode).find('span.translation').on('keydown.translation', (e) ->
|
||||
e.stopPropagation()
|
||||
return true
|
||||
)
|
||||
)
|
||||
|
||||
mutation.removedNodes.forEach((removedNode) ->
|
||||
$(removedNode).find('span.translation').off('.translation')
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@observer.observe(document.body, {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
})
|
||||
|
||||
# rerender controllers
|
||||
App.Event.trigger('ui:rerender')
|
||||
|
||||
@@ -93,7 +120,9 @@ class Widget extends App.Controller
|
||||
element
|
||||
|
||||
disable: ->
|
||||
$('body').off('focus.translation blur.translation')
|
||||
@observer.disconnect()
|
||||
|
||||
$('body').off('.translation')
|
||||
|
||||
# disable translation inline
|
||||
App.Config.set('translation_inline', false)
|
||||
|
||||
@@ -236,7 +236,7 @@ class _i18nSingleton extends Spine.Module
|
||||
return string if !string
|
||||
|
||||
if App.Config.get('translation_inline')
|
||||
return '<span class="translation" onkeydown="arguments[0].stopPropagation(); return true" onclick="arguments[0].stopPropagation(); return false" contenteditable="true" title="' + App.Utils.htmlEscape(string) + '">' + App.Utils.htmlEscape(@translate(string)) + '</span>'
|
||||
return '<span class="translation" contenteditable="true" title="' + App.Utils.htmlEscape(string) + '">' + App.Utils.htmlEscape(@translate(string)) + '</span>'
|
||||
|
||||
translated = @translate(string, args, true, true)
|
||||
|
||||
|
||||
@@ -679,7 +679,8 @@ class App.Utils
|
||||
$('<div/>').html(message).contents().each (index, node) ->
|
||||
text = $(node).text()
|
||||
if node.nodeType == Node.TEXT_NODE
|
||||
res.push text
|
||||
# convert text back to HTML as it was before
|
||||
res.push $('<div>').text(text).html()
|
||||
if text.trim().length
|
||||
contentNodes.push index
|
||||
else if node.nodeType == Node.ELEMENT_NODE
|
||||
|
||||
@@ -193,6 +193,7 @@ App.ViewHelpers =
|
||||
|
||||
canDownload: (contentType) ->
|
||||
contentType = App.Utils.contentTypeCleanup(contentType)
|
||||
return false if contentType is 'application/pdf'
|
||||
contentType != 'text/html'
|
||||
|
||||
canPreview: (contentType) ->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class App.Chat extends App.Model
|
||||
@configure 'Chat', 'name', 'active', 'public', 'max_queue', 'block_ip', 'block_country', 'note'
|
||||
@configure 'Chat', 'name', 'active', 'public', 'max_queue', 'block_ip', 'whitelisted_websites', 'block_country', 'note'
|
||||
@extend Spine.Model.Ajax
|
||||
@url: @apiPath + '/chats'
|
||||
@countries:
|
||||
@@ -250,6 +250,7 @@ class App.Chat extends App.Model
|
||||
{ name: 'note', display: 'Note', tag: 'textarea', limit: 250, null: true },
|
||||
{ name: 'max_queue', display: 'Max. clients in waitlist', tag: 'input', default: 2 },
|
||||
{ name: 'block_ip', display: 'Blocked IPs (separated by ;)', tag: 'input', default: '', null: true },
|
||||
{ name: 'whitelisted_websites', display: 'Allow websites (separated by ;)', tag: 'input', default: '', null: true },
|
||||
{ name: 'block_country', display: 'Blocked countries', tag: 'column_select', multiple: true, null: true, default: '', options: @countries, seperator: ';' },
|
||||
{ name: 'active', display: 'Active', tag: 'active', default: true },
|
||||
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 },
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
<% if @sent: %>
|
||||
<h2><%- @T('We\'ve sent password reset instructions to your email address.') %><small></small></h2>
|
||||
<p><%- @T('If you don\'t receive instructions within a minute or two, check your email\'s spam and junk filters, or try resending your request.') %></p>
|
||||
<a href="#" class="subtle retry">» <%- @T('again') %> «</a>
|
||||
<div class="form-controls">
|
||||
<a class="btn btn--text btn--subtle js-cancel" href="#login"><%- @T( 'Cancel & Go Back' ) %></a>
|
||||
<button class="btn btn--primary retry align-right"><%- @T( 'again' ) %></button>
|
||||
</div>
|
||||
<% else: %>
|
||||
<h2><%- @T( 'Forgot your password?' ) %><small></small></h2>
|
||||
<form class="form-password">
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<small class="task-subline">
|
||||
<%- @C('ticket_hook') %> <span class="ticket-number js-objectNumber" data-number="<%- @C('ticket_hook') %><%- @ticket.number %>"><%- @ticket.number %></span> - <%- @T('created') %> <%- @humanTime(@ticket.created_at) %> <% if !@isCustomer && @ticket.escalation_at: %> - <%- @T('escalation') %> <%- @humanTime(@ticket.escalation_at, true) %><% end %>
|
||||
</small>
|
||||
<%- @C('ticket_hook') %> <span class="ticket-number js-objectNumber" data-number="<%- @C('ticket_hook') %><%= @ticket.number %>"><%= @ticket.number %></span> - <%- @T('created') %> <%- @humanTime(@ticket.created_at) %> <% if !@isCustomer && @ticket.escalation_at: %> - <%- @T('escalation') %> <%- @humanTime(@ticket.escalation_at, true) %><% end %>
|
||||
</small>
|
||||
|
||||
@@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base
|
||||
include ApplicationController::HasUser
|
||||
include ApplicationController::HasResponseExtentions
|
||||
include ApplicationController::PreventsCsrf
|
||||
include ApplicationController::HasSecureContentSecurityPolicyForDownloads
|
||||
include ApplicationController::LogsHttpAccess
|
||||
include ApplicationController::ChecksAccess
|
||||
end
|
||||
|
||||
@@ -75,8 +75,18 @@ module ApplicationController::HandlesErrors
|
||||
data[:error_human] = data[:error]
|
||||
end
|
||||
|
||||
if Rails.env.production? && data[:error_human].present?
|
||||
data[:error] = data.delete(:error_human)
|
||||
if Rails.env.production?
|
||||
if data[:error_human].present?
|
||||
data[:error] = data.delete(:error_human)
|
||||
else
|
||||
# We want to avoid leaking of internal information but also want the user
|
||||
# to give the administrator a reference to find the cause of the error.
|
||||
# Therefore we generate a one time unique error ID that can be used to
|
||||
# search the logs and find the actual error message.
|
||||
error_code_prefix = "Error ID #{SecureRandom.urlsafe_base64(6)}:"
|
||||
Rails.logger.error "#{error_code_prefix} #{data[:error]}"
|
||||
data[:error] = "#{error_code_prefix} Please contact your administrator."
|
||||
end
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
module ApplicationController::HasSecureContentSecurityPolicyForDownloads
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
||||
around_action do |_controller, block|
|
||||
|
||||
subscriber = proc do
|
||||
policy = ActionDispatch::ContentSecurityPolicy.new
|
||||
policy.default_src :none
|
||||
policy.plugin_types 'application/pdf'
|
||||
|
||||
request.content_security_policy = policy
|
||||
end
|
||||
|
||||
ActiveSupport::Notifications.subscribed(subscriber, 'send_file.action_controller') do
|
||||
ActiveSupport::Notifications.subscribed(subscriber, 'send_data.action_controller') do
|
||||
block.call
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,7 @@ module ApplicationController::SetsHeaders
|
||||
|
||||
included do
|
||||
before_action :cors_preflight_check
|
||||
after_action :set_access_control_headers
|
||||
after_action :set_access_control_headers, :set_cache_control_headers
|
||||
end
|
||||
|
||||
private
|
||||
@@ -22,23 +22,25 @@ module ApplicationController::SetsHeaders
|
||||
headers['Access-Control-Allow-Headers'] = 'Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control, Accept-Language'
|
||||
end
|
||||
|
||||
def set_cache_control_headers
|
||||
|
||||
# by default http cache is disabled
|
||||
# expires_now function only sets no-cache so we handle the headers by our own.
|
||||
headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
|
||||
headers['Pragma'] = 'no-cache'
|
||||
headers['Expires'] = '-1'
|
||||
end
|
||||
|
||||
# If this is a preflight OPTIONS request, then short-circuit the
|
||||
# request, return only the necessary headers and return an empty
|
||||
# text/plain.
|
||||
def cors_preflight_check
|
||||
return true if @_auth_type != 'token_auth' && @_auth_type != 'basic_auth'
|
||||
|
||||
cors_preflight_check_execute
|
||||
end
|
||||
|
||||
def cors_preflight_check_execute
|
||||
return true if request.method != 'OPTIONS'
|
||||
return if request.method != 'OPTIONS'
|
||||
|
||||
headers['Access-Control-Allow-Origin'] = '*'
|
||||
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, PATCH, OPTIONS'
|
||||
headers['Access-Control-Allow-Headers'] = 'Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control, Accept-Language'
|
||||
headers['Access-Control-Max-Age'] = '1728000'
|
||||
render text: '', content_type: 'text/plain'
|
||||
false
|
||||
render plain: ''
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
class FormController < ApplicationController
|
||||
skip_before_action :verify_csrf_token
|
||||
before_action :cors_preflight_check_execute
|
||||
before_action :cors_preflight_check
|
||||
after_action :set_access_control_headers_execute
|
||||
skip_before_action :user_device_check
|
||||
|
||||
|
||||
@@ -9,29 +9,12 @@ class TestsController < ApplicationController
|
||||
render json: result
|
||||
end
|
||||
|
||||
# GET /test/unprocessable_entity
|
||||
def error_unprocessable_entity
|
||||
raise Exceptions::UnprocessableEntity, 'some error message'
|
||||
end
|
||||
# GET /test/raised_exception
|
||||
def error_raised_exception
|
||||
exception = params.fetch(:exception, 'StandardError')
|
||||
message = params.fetch(:message, 'no message provided')
|
||||
|
||||
# GET /test/not_authorized
|
||||
def error_not_authorized
|
||||
raise Exceptions::NotAuthorized, 'some error message'
|
||||
end
|
||||
|
||||
# GET /test/ar_not_found
|
||||
def error_ar_not_found
|
||||
raise ActiveRecord::RecordNotFound, 'some error message'
|
||||
end
|
||||
|
||||
# GET /test/standard_error
|
||||
def error_standard_error
|
||||
raise StandardError, 'some error message'
|
||||
end
|
||||
|
||||
# GET /test/argument_error
|
||||
def error_argument_error
|
||||
raise ArgumentError, 'some error message'
|
||||
raise exception.safe_constantize, message
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -281,8 +281,9 @@ class TicketsController < ApplicationController
|
||||
|
||||
# return result
|
||||
result = Ticket::ScreenOptions.list_by_customer(
|
||||
customer_id: params[:customer_id],
|
||||
limit: 15,
|
||||
current_user: current_user,
|
||||
customer_id: params[:customer_id],
|
||||
limit: 15,
|
||||
)
|
||||
render json: result
|
||||
end
|
||||
|
||||
@@ -534,9 +534,12 @@ curl http://localhost/api/v1/users/email_verify_send -v -u #{login}:#{password}
|
||||
|
||||
raise Exceptions::UnprocessableEntity, 'No email!' if !params[:email]
|
||||
|
||||
# check is verify is possible to send
|
||||
user = User.find_by(email: params[:email].downcase)
|
||||
raise Exceptions::UnprocessableEntity, 'No such user!' if !user
|
||||
if !user
|
||||
# result is always positive to avoid leaking of existing user accounts
|
||||
render json: { message: 'ok' }, status: :ok
|
||||
return
|
||||
end
|
||||
|
||||
#if user.verified == true
|
||||
# render json: { error: 'Already verified!' }, status: :unprocessable_entity
|
||||
@@ -610,14 +613,10 @@ curl http://localhost/api/v1/users/password_reset -v -u #{login}:#{password} -H
|
||||
render json: { message: 'ok', token: result[:token].name }, status: :ok
|
||||
return
|
||||
end
|
||||
|
||||
# token sent to user, send ok to browser
|
||||
render json: { message: 'ok' }, status: :ok
|
||||
return
|
||||
end
|
||||
|
||||
# unable to generate token
|
||||
render json: { message: 'failed' }, status: :ok
|
||||
# result is always positive to avoid leaking of existing user accounts
|
||||
render json: { message: 'ok' }, status: :ok
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
@@ -236,9 +236,7 @@ returns
|
||||
|
||||
def filter_attributes(attributes)
|
||||
# remove forbidden attributes
|
||||
%w[password token tokens token_ids].each do |item|
|
||||
attributes.delete(item)
|
||||
end
|
||||
attributes.except!('password', 'token', 'tokens', 'token_ids')
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
@@ -234,9 +234,10 @@ example
|
||||
NOTICE
|
||||
Rails.logger.error "Net::IMAP failed to parse message #{message_id}: #{e.message} (#{e.class})"
|
||||
Rails.logger.error '(See https://github.com/zammad/zammad/issues/2754 for more details)'
|
||||
next
|
||||
end
|
||||
|
||||
next if message_meta.nil?
|
||||
|
||||
# ignore verify messages
|
||||
next if !messages_is_too_old_verify?(message_meta, count, count_all)
|
||||
|
||||
|
||||
@@ -617,6 +617,23 @@ check if ip address is blocked for chat
|
||||
|
||||
=begin
|
||||
|
||||
check if website is allowed for chat
|
||||
|
||||
chat = Chat.find(123)
|
||||
chat.website_whitelisted?('zammad.org')
|
||||
|
||||
=end
|
||||
|
||||
def website_whitelisted?(website)
|
||||
return true if whitelisted_websites.blank?
|
||||
|
||||
whitelisted_websites.split(';').any? do |whitelisted_website|
|
||||
website.downcase.include?(whitelisted_website.downcase.strip)
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
check if country is blocked for chat
|
||||
|
||||
chat = Chat.find(123)
|
||||
@@ -633,10 +650,9 @@ check if country is blocked for chat
|
||||
return false if geo_ip['country_code'].blank?
|
||||
|
||||
countries = block_country.split(';')
|
||||
countries.each do |local_country|
|
||||
return true if geo_ip['country_code'] == local_country
|
||||
countries.any? do |local_country|
|
||||
geo_ip['country_code'] == local_country
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -897,34 +897,49 @@ is certain attribute used by triggers, overviews or schedulers
|
||||
def check_name
|
||||
return if !name
|
||||
|
||||
raise 'Name can\'t get used, *_id and *_ids are not allowed' if name.match?(/.+?_(id|ids)$/i)
|
||||
raise 'Spaces in name are not allowed' if name.match?(/\s/)
|
||||
raise 'Only letters from a-z, numbers from 0-9, and _ are allowed' if !name.match?(/^[a-z0-9_]+$/)
|
||||
raise 'At least one letters is needed' if !name.match?(/[a-z]/)
|
||||
if name.match?(/.+?_(id|ids)$/i)
|
||||
errors.add(:name, "can't get used because *_id and *_ids are not allowed")
|
||||
end
|
||||
if name.match?(/\s/)
|
||||
errors.add(:name, 'spaces are not allowed')
|
||||
end
|
||||
if !name.match?(/^[a-z0-9_]+$/)
|
||||
errors.add(:name, 'Only letters from a-z because numbers from 0-9 and _ are allowed')
|
||||
end
|
||||
if !name.match?(/[a-z]/)
|
||||
errors.add(:name, 'At least one letters is needed')
|
||||
end
|
||||
|
||||
# do not allow model method names as attributes
|
||||
reserved_words = %w[destroy true false integer select drop create alter index table varchar blob date datetime timestamp url icon initials avatar permission validate subscribe unsubscribe translate search _type _doc _id id]
|
||||
raise "#{name} is a reserved word, please choose a different one" if name.match?(/^(#{reserved_words.join('|')})$/)
|
||||
if name.match?(/^(#{reserved_words.join('|')})$/)
|
||||
errors.add(:name, "#{name} is a reserved word! (1)")
|
||||
end
|
||||
|
||||
# fixes issue #2236 - Naming an attribute "attribute" causes ActiveRecord failure
|
||||
begin
|
||||
ObjectLookup.by_id(object_lookup_id).constantize.instance_method_already_implemented? name
|
||||
rescue ActiveRecord::DangerousAttributeError
|
||||
raise "#{name} is a reserved word, please choose a different one"
|
||||
errors.add(:name, "#{name} is a reserved word! (2)")
|
||||
end
|
||||
|
||||
record = object_lookup.name.constantize.new
|
||||
return true if !record.respond_to?(name.to_sym)
|
||||
raise "#{name} already exists!" if record.attributes.key?(name) && new_record?
|
||||
return true if record.attributes.key?(name)
|
||||
if record.respond_to?(name.to_sym) && record.attributes.key?(name) && new_record?
|
||||
errors.add(:name, "#{name} already exists!")
|
||||
end
|
||||
|
||||
raise "#{name} is a reserved word, please choose a different one"
|
||||
if errors.present?
|
||||
raise ActiveRecord::RecordInvalid, self
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def check_editable
|
||||
return if editable
|
||||
|
||||
raise 'Attribute not editable!'
|
||||
errors.add(:name, 'Attribute not editable!')
|
||||
raise ActiveRecord::RecordInvalid, self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -180,11 +180,16 @@ returns
|
||||
state_id_list_open = Ticket::State.by_category(:open).pluck(:id)
|
||||
state_id_list_closed = Ticket::State.by_category(:closed).pluck(:id)
|
||||
|
||||
# open tickets by customer
|
||||
access_condition = Ticket.access_condition(data[:current_user], 'read')
|
||||
|
||||
# get tickets
|
||||
tickets_open = Ticket.where(
|
||||
customer_id: data[:customer_id],
|
||||
state_id: state_id_list_open
|
||||
).limit(data[:limit] || 15).order(created_at: :desc)
|
||||
)
|
||||
.where(access_condition)
|
||||
.limit(data[:limit] || 15).order(created_at: :desc)
|
||||
assets = {}
|
||||
ticket_ids_open = []
|
||||
tickets_open.each do |ticket|
|
||||
@@ -195,7 +200,9 @@ returns
|
||||
tickets_closed = Ticket.where(
|
||||
customer_id: data[:customer_id],
|
||||
state_id: state_id_list_closed
|
||||
).limit(data[:limit] || 15).order(created_at: :desc)
|
||||
)
|
||||
.where(access_condition)
|
||||
.limit(data[:limit] || 15).order(created_at: :desc)
|
||||
ticket_ids_closed = []
|
||||
tickets_closed.each do |ticket|
|
||||
ticket_ids_closed.push ticket.id
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- svgstore fallback -->
|
||||
<script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
/*
|
||||
detect if browser is
|
||||
- Chrome 14-20
|
||||
@@ -27,9 +27,9 @@
|
||||
xhr.open('get', 'assets/images/icons.svg', true)
|
||||
xhr.send()
|
||||
})(document)
|
||||
</script>
|
||||
<% end -%>
|
||||
<div id="app"></div>
|
||||
<div class="splash">
|
||||
<svg class="icon icon-logo"><use xlink:href="assets/images/icons.svg#icon-logo" /></svg>
|
||||
<div class="splash-title">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<%= stylesheet_link_tag "application", :media => 'all' %>
|
||||
<%= stylesheet_link_tag "application-print", :media => 'print' %>
|
||||
<% if Rails.configuration.assets.debug %>
|
||||
<%= javascript_include_tag "application" %>
|
||||
<%= javascript_include_tag "application", nonce: true %>
|
||||
<% else %>
|
||||
<%= javascript_include_tag "application", :defer => 'defer' %>
|
||||
<%= javascript_include_tag "application", nonce: true, :defer => 'defer' %>
|
||||
<% end %>
|
||||
<%= csrf_meta_tags %>
|
||||
</head>
|
||||
|
||||
@@ -70,10 +70,13 @@
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script type='text/javascript'>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
if(window.fetch === undefined || window.Promise === undefined || Element.prototype.prepend === undefined){
|
||||
document.write('<script src="<%= path_to_javascript('knowledge_base_public_polyfills') %>"><\/script>')
|
||||
var polyfillScriptTag = document.createElement('script');
|
||||
polyfillScriptTag.setAttribute('src', '<%= path_to_javascript('knowledge_base_public_polyfills') %>');
|
||||
polyfillScriptTag.setAttribute('nonce', '<%= content_security_policy_nonce %>');
|
||||
document.body.appendChild(polyfillScriptTag);
|
||||
}
|
||||
</script>
|
||||
<% end -%>
|
||||
|
||||
<%= javascript_include_tag 'knowledge_base_public' %>
|
||||
<%= javascript_include_tag 'knowledge_base_public', nonce: true %>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/core.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/core.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +7,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_column_select.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_column_select.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_extended.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_extended.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_find.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_find.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_searchable_select.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_searchable_select.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_ticket_perform_action.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_ticket_perform_action.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_timer.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_timer.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_tree_select.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_tree_select.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_trim.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_trim.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/form_validation.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/form_validation.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
@@ -19,4 +18,4 @@ body {
|
||||
<div id="forms"></div>
|
||||
<button type="submit" class="btn btn-primary submit">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/html_utils.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/html_utils.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/local_storage.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/local_storage.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/model.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/model.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/model_binding.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/model_binding.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/model_ticket.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/model_ticket.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/model_ui.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/model_ui.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,7 +8,7 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/session.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/session.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/table.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/table.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,9 +8,9 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
<div id="table"></div>
|
||||
<div id="table"></div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/table_extended.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/table_extended.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,9 +8,9 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
<div id="table"></div>
|
||||
<div id="table"></div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/taskbar.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/taskbar.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/text_module.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/text_module.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,8 +8,8 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/ticket_selector.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/ticket_selector.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,7 +8,7 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<script src="/assets/tests/qunit-1.21.0.js"></script>
|
||||
<script src="/assets/tests/ui.js"></script>
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/ui.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@@ -9,7 +8,7 @@ body {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
<% end -%>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
||||
|
||||
@@ -16,8 +16,29 @@
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
|
||||
Rails.application.config.content_security_policy do |policy|
|
||||
base_uri = proc do
|
||||
next if !Rails.env.production?
|
||||
next if !Setting.get('system_init_done')
|
||||
|
||||
http_type = Setting.get('http_type')
|
||||
fqdn = Setting.get('fqdn')
|
||||
|
||||
"#{http_type}://#{fqdn}"
|
||||
end
|
||||
|
||||
policy.base_uri :self, base_uri
|
||||
|
||||
policy.default_src :self, :ws, :wss, 'https://log.zammad.com', 'https://images.zammad.com'
|
||||
policy.font_src :self, :data
|
||||
policy.img_src '*', :data
|
||||
policy.object_src :none
|
||||
policy.script_src :self, :unsafe_eval, :unsafe_inline, :strict_dynamic
|
||||
policy.style_src :self, :unsafe_inline
|
||||
end
|
||||
|
||||
# If you are using UJS then enable automatic nonce generation
|
||||
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
||||
Rails.application.config.content_security_policy_nonce_generator = ->(_request) { SecureRandom.base64(16) }
|
||||
|
||||
# Report CSP violations to a specified URI
|
||||
# For further information see the following documentation:
|
||||
|
||||
@@ -6,6 +6,7 @@ Rails.application.routes.draw do
|
||||
|
||||
# just remember to delete public/index.html.
|
||||
root to: 'init#index', via: :get
|
||||
root to: 'errors#routing', via: %i[post put delete options]
|
||||
|
||||
# load routes from external files
|
||||
dir = File.expand_path(__dir__)
|
||||
@@ -18,6 +19,5 @@ Rails.application.routes.draw do
|
||||
end
|
||||
end
|
||||
|
||||
match '*a', to: 'errors#routing', via: %i[get post put delete]
|
||||
|
||||
match '*a', to: 'errors#routing', via: %i[get post put delete options]
|
||||
end
|
||||
|
||||
@@ -25,10 +25,6 @@ Zammad::Application.routes.draw do
|
||||
match '/tests_taskbar', to: 'tests#taskbar', via: :get
|
||||
match '/tests_text_module', to: 'tests#text_module', via: :get
|
||||
match '/tests/wait/:sec', to: 'tests#wait', via: :get
|
||||
match '/tests/unprocessable_entity', to: 'tests#error_unprocessable_entity', via: :get
|
||||
match '/tests/not_authorized', to: 'tests#error_not_authorized', via: :get
|
||||
match '/tests/ar_not_found', to: 'tests#error_ar_not_found', via: :get
|
||||
match '/tests/standard_error', to: 'tests#error_standard_error', via: :get
|
||||
match '/tests/argument_error', to: 'tests#error_argument_error', via: :get
|
||||
match '/tests/raised_exception', to: 'tests#error_raised_exception', via: :get
|
||||
|
||||
end
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
# replace 'localhost' with your fqdn if you want to use zammad from remote
|
||||
ServerName localhost
|
||||
|
||||
# security - prevent information disclosure about server version
|
||||
ServerTokens Prod
|
||||
|
||||
## don't loose time with IP address lookups
|
||||
HostnameLookups Off
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName example.com
|
||||
|
||||
# security - prevent information disclosure about server version
|
||||
ServerTokens Prod
|
||||
|
||||
Redirect permanent / https://example.com
|
||||
</VirtualHost>
|
||||
|
||||
@@ -25,6 +29,9 @@
|
||||
# replace 'localhost' with your fqdn if you want to use zammad from remote
|
||||
ServerName localhost
|
||||
|
||||
# security - prevent information disclosure about server version
|
||||
ServerTokens Prod
|
||||
|
||||
## don't loose time with IP address lookups
|
||||
HostnameLookups Off
|
||||
|
||||
@@ -46,7 +53,7 @@
|
||||
ProxyPass /robots.txt !
|
||||
ProxyPass /ws ws://127.0.0.1:6042/
|
||||
ProxyPass / http://127.0.0.1:3000/
|
||||
|
||||
|
||||
# Use settings below if proxying does not work and you receive HTTP-Errror 404
|
||||
# if you use the settings below, make sure to comment out the above two options
|
||||
# This may not apply to all systems, applies to openSuse
|
||||
|
||||
@@ -16,6 +16,9 @@ server {
|
||||
# replace 'localhost' with your fqdn if you want to use zammad from remote
|
||||
server_name localhost;
|
||||
|
||||
# security - prevent information disclosure about server version
|
||||
server_tokens off;
|
||||
|
||||
root /opt/zammad/public;
|
||||
|
||||
access_log /var/log/nginx/zammad.access.log;
|
||||
|
||||
@@ -21,6 +21,9 @@ server {
|
||||
|
||||
server_name example.com;
|
||||
|
||||
# security - prevent information disclosure about server version
|
||||
server_tokens off;
|
||||
|
||||
access_log /var/log/nginx/zammad.access.log;
|
||||
error_log /var/log/nginx/zammad.error.log;
|
||||
|
||||
@@ -38,6 +41,9 @@ server {
|
||||
|
||||
server_name example.com;
|
||||
|
||||
# security - prevent information disclosure about server version
|
||||
server_tokens off;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/example.com-fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/example.com-privkey.pem;
|
||||
|
||||
|
||||
@@ -483,6 +483,7 @@ class CreateTicket < ActiveRecord::Migration[4.2]
|
||||
t.boolean :public, null: false, default: false
|
||||
t.string :block_ip, limit: 5000, null: true
|
||||
t.string :block_country, limit: 5000, null: true
|
||||
t.string :whitelisted_websites, limit: 5000, null: true
|
||||
t.string :preferences, limit: 5000, null: true
|
||||
t.integer :updated_by_id, null: false
|
||||
t.integer :created_by_id, null: false
|
||||
|
||||
@@ -4,13 +4,20 @@ class Issue2460FixCorruptedTwitterIds < ActiveRecord::Migration[5.2]
|
||||
|
||||
Channel.where(area: 'Twitter::Account').each do |channel|
|
||||
|
||||
client = Twitter::REST::Client.new do |config|
|
||||
config.consumer_key = channel.options['auth']['consumer_key']
|
||||
config.consumer_secret = channel.options['auth']['consumer_secret']
|
||||
config.access_token = channel.options['auth']['oauth_token']
|
||||
config.access_token_secret = channel.options['auth']['oauth_token_secret']
|
||||
client = nil
|
||||
begin
|
||||
client = Twitter::REST::Client.new do |config|
|
||||
config.consumer_key = channel.options['auth']['consumer_key']
|
||||
config.consumer_secret = channel.options['auth']['consumer_secret']
|
||||
config.access_token = channel.options['auth']['oauth_token']
|
||||
config.access_token_secret = channel.options['auth']['oauth_token_secret']
|
||||
end
|
||||
rescue => e
|
||||
Rails.logger.error "Error while trying to update corrupted Twitter User ID: #{e.message}"
|
||||
end
|
||||
|
||||
next if client.nil?
|
||||
|
||||
channel.options['user']['id'] = client.user.id.to_s
|
||||
|
||||
channel.save!
|
||||
|
||||
9
db/migrate/20200205000001_chat_add_allow_website.rb
Normal file
9
db/migrate/20200205000001_chat_add_allow_website.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class ChatAddAllowWebsite < ActiveRecord::Migration[5.1]
|
||||
def up
|
||||
|
||||
# return if it's a new setup
|
||||
return if !Setting.find_by(name: 'system_init_done')
|
||||
|
||||
add_column :chats, :whitelisted_websites, :string, limit: 5000, null: true
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
module EmailHelper
|
||||
class EmailHelper
|
||||
|
||||
=begin
|
||||
|
||||
@@ -454,6 +454,24 @@ returns
|
||||
password: password,
|
||||
},
|
||||
},
|
||||
{
|
||||
adapter: 'smtp',
|
||||
options: {
|
||||
host: domain,
|
||||
port: 587,
|
||||
user: user,
|
||||
password: password,
|
||||
},
|
||||
},
|
||||
{
|
||||
adapter: 'smtp',
|
||||
options: {
|
||||
host: domain,
|
||||
port: 587,
|
||||
user: email,
|
||||
password: password,
|
||||
},
|
||||
},
|
||||
]
|
||||
outbounds = outbounds.concat(outbound)
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module EmailHelper
|
||||
class EmailHelper
|
||||
class Probe
|
||||
|
||||
=begin
|
||||
@@ -400,6 +400,7 @@ returns on fail
|
||||
'Incorrect username' => 'Authentication failed, username incorrect!',
|
||||
'Lookup failed' => 'Authentication failed, username incorrect!',
|
||||
'Invalid credentials' => 'Authentication failed, invalid credentials!',
|
||||
'authentication not enabled' => 'Authentication not possible (not offered by the service)',
|
||||
'getaddrinfo: nodename nor servname provided, or not known' => 'Hostname not found!',
|
||||
'getaddrinfo: Name or service not known' => 'Hostname not found!',
|
||||
'No route to host' => 'No route to host!',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module EmailHelper
|
||||
class EmailHelper
|
||||
class Verify
|
||||
|
||||
=begin
|
||||
|
||||
@@ -3,7 +3,7 @@ module SessionHelper
|
||||
collections, assets = default_collections(user)
|
||||
|
||||
{
|
||||
session: user,
|
||||
session: user.filter_attributes(user.attributes),
|
||||
models: models(user),
|
||||
collections: collections,
|
||||
assets: assets,
|
||||
|
||||
@@ -105,6 +105,14 @@ class Sessions::Event::Base
|
||||
user
|
||||
end
|
||||
|
||||
def remote_ip
|
||||
@headers&.fetch('X-Forwarded-For', nil).presence
|
||||
end
|
||||
|
||||
def origin
|
||||
@headers&.fetch('Origin', nil).presence
|
||||
end
|
||||
|
||||
def permission_check(key, event)
|
||||
user = current_user
|
||||
return if !user
|
||||
|
||||
@@ -24,17 +24,17 @@ return is sent as message back to peer
|
||||
|
||||
# geo ip lookup
|
||||
geo_ip = nil
|
||||
if @remote_ip
|
||||
geo_ip = Service::GeoIp.location(@remote_ip)
|
||||
if remote_ip
|
||||
geo_ip = Service::GeoIp.location(remote_ip)
|
||||
end
|
||||
|
||||
# dns lookup
|
||||
dns_name = nil
|
||||
if @remote_ip
|
||||
if remote_ip
|
||||
begin
|
||||
dns = Resolv::DNS.new
|
||||
dns.timeouts = 3
|
||||
result = dns.getname @remote_ip
|
||||
result = dns.getname remote_ip
|
||||
if result
|
||||
dns_name = result.to_s
|
||||
end
|
||||
@@ -51,7 +51,7 @@ return is sent as message back to peer
|
||||
preferences: {
|
||||
url: @payload['data']['url'],
|
||||
participants: [@client_id],
|
||||
remote_ip: @remote_ip,
|
||||
remote_ip: remote_ip,
|
||||
geo_ip: geo_ip,
|
||||
dns_name: dns_name,
|
||||
},
|
||||
|
||||
@@ -21,8 +21,9 @@ return is sent as message back to peer
|
||||
def run
|
||||
return super if super
|
||||
return if !check_chat_exists
|
||||
return if !check_chat_block_by_ip
|
||||
return if !check_chat_block_by_country
|
||||
return if blocked_ip?
|
||||
return if blocked_country?
|
||||
return if blocked_origin?
|
||||
|
||||
# check if it's a chat sessin reconnect
|
||||
session_id = nil
|
||||
@@ -54,10 +55,28 @@ return is sent as message back to peer
|
||||
}
|
||||
end
|
||||
|
||||
def check_chat_block_by_ip
|
||||
chat = current_chat
|
||||
return true if !chat.blocked_ip?(@remote_ip)
|
||||
def blocked_ip?
|
||||
return false if !current_chat.blocked_ip?(remote_ip)
|
||||
|
||||
send_unavailable
|
||||
true
|
||||
end
|
||||
|
||||
def blocked_country?
|
||||
return false if !current_chat.blocked_country?(remote_ip)
|
||||
|
||||
send_unavailable
|
||||
true
|
||||
end
|
||||
|
||||
def blocked_origin?
|
||||
return false if current_chat.website_whitelisted?(origin)
|
||||
|
||||
send_unavailable
|
||||
true
|
||||
end
|
||||
|
||||
def send_unavailable
|
||||
error = {
|
||||
event: 'chat_error',
|
||||
data: {
|
||||
@@ -65,21 +84,5 @@ return is sent as message back to peer
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
false
|
||||
end
|
||||
|
||||
def check_chat_block_by_country
|
||||
chat = current_chat
|
||||
return true if !chat.blocked_country?(@remote_ip)
|
||||
|
||||
error = {
|
||||
event: 'chat_error',
|
||||
data: {
|
||||
state: 'chat_unavailable',
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, error)
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -79,6 +79,18 @@ namespace :searchindex do
|
||||
pipeline = "zammad#{rand(999_999_999_999)}"
|
||||
Setting.set('es_pipeline', pipeline)
|
||||
end
|
||||
|
||||
# define pipeline_field_attributes
|
||||
# ES 5.6 and nower has no ignore_missing support
|
||||
pipeline_field_attributes = {
|
||||
ignore_failure: true,
|
||||
}
|
||||
if es_multi_index?
|
||||
pipeline_field_attributes = {
|
||||
ignore_failure: true,
|
||||
ignore_missing: true,
|
||||
}
|
||||
end
|
||||
print 'create pipeline (pipeline)... '
|
||||
SearchIndexBackend.processors(
|
||||
"_ingest/pipeline/#{pipeline}": [
|
||||
@@ -91,22 +103,19 @@ namespace :searchindex do
|
||||
processors: [
|
||||
{
|
||||
foreach: {
|
||||
field: 'article',
|
||||
ignore_failure: true,
|
||||
processor: {
|
||||
field: 'article',
|
||||
processor: {
|
||||
foreach: {
|
||||
field: '_ingest._value.attachment',
|
||||
ignore_failure: true,
|
||||
processor: {
|
||||
field: '_ingest._value.attachment',
|
||||
processor: {
|
||||
attachment: {
|
||||
target_field: '_ingest._value',
|
||||
field: '_ingest._value._content',
|
||||
ignore_failure: true,
|
||||
}
|
||||
target_field: '_ingest._value',
|
||||
field: '_ingest._value._content',
|
||||
}.merge(pipeline_field_attributes),
|
||||
}
|
||||
}
|
||||
}.merge(pipeline_field_attributes),
|
||||
}
|
||||
}
|
||||
}.merge(pipeline_field_attributes),
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -301,7 +310,7 @@ def es_pipeline?
|
||||
true
|
||||
end
|
||||
|
||||
# no mulit index for elasticsearch 5.6 and lower
|
||||
# no multi index for elasticsearch 5.6 and lower
|
||||
def es_multi_index?
|
||||
number = es_version
|
||||
return false if number.blank?
|
||||
|
||||
@@ -444,7 +444,7 @@ class TwitterSync
|
||||
|
||||
=begin
|
||||
|
||||
create a tweet ot direct message from an article
|
||||
create a tweet or direct message from an article
|
||||
|
||||
=end
|
||||
|
||||
@@ -484,6 +484,12 @@ create a tweet ot direct message from an article
|
||||
|
||||
Rails.logger.debug { 'Create tweet from article...' }
|
||||
|
||||
# rubocop:disable Style/AsciiComments
|
||||
# workaround for https://github.com/sferik/twitter/issues/677
|
||||
# https://github.com/zammad/zammad/issues/2873 - unable to post
|
||||
# tweets with * - replace `*` with the wide-asterisk `*`.
|
||||
# rubocop:enable Style/AsciiComments
|
||||
article[:body].tr!('*', '*') if article[:body].present?
|
||||
tweet = @client.update(
|
||||
article[:body],
|
||||
{
|
||||
|
||||
@@ -48,7 +48,6 @@ class WebsocketServer
|
||||
|
||||
def self.onopen(websocket, handshake)
|
||||
headers = handshake.headers
|
||||
remote_ip = get_remote_ip(headers)
|
||||
client_id = websocket.object_id.to_s
|
||||
log 'notice', 'Client connected.', client_id
|
||||
Sessions.create( client_id, {}, { type: 'websocket' } )
|
||||
@@ -60,7 +59,6 @@ class WebsocketServer
|
||||
last_ping: Time.now.utc.to_i,
|
||||
error_count: 0,
|
||||
headers: headers,
|
||||
remote_ip: remote_ip,
|
||||
}
|
||||
end
|
||||
|
||||
@@ -103,7 +101,7 @@ class WebsocketServer
|
||||
event: data['event'],
|
||||
payload: data,
|
||||
session: @clients[client_id][:session],
|
||||
remote_ip: @clients[client_id][:remote_ip],
|
||||
headers: @clients[client_id][:headers],
|
||||
client_id: client_id,
|
||||
clients: @clients,
|
||||
options: @options,
|
||||
@@ -116,12 +114,6 @@ class WebsocketServer
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_remote_ip(headers)
|
||||
return headers['X-Forwarded-For'] if headers && headers['X-Forwarded-For']
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def self.websocket_send(client_id, data)
|
||||
msg = if data.class != Array
|
||||
"[#{data.to_json}]"
|
||||
|
||||
@@ -371,6 +371,23 @@ do($ = window.jQuery, window) ->
|
||||
'Since you didn\'t respond in the last %s minutes your conversation with %s got closed.': 'Поскольку вы не отвечали в течение последних %s минут, ваш разговор с %s был закрыт.'
|
||||
'Since you didn\'t respond in the last %s minutes your conversation got closed.': 'Поскольку вы не отвечали в течение последних %s минут, ваш разговор был закрыт.'
|
||||
'We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you!': 'К сожалению, ожидание свободного места требует больше времени. Повторите попытку позже или отправьте нам электронное письмо. Спасибо!'
|
||||
'sv':
|
||||
'<strong>Chat</strong> with us!': '<strong>Chatta</strong> med oss!'
|
||||
'Scroll down to see new messages': 'Rulla ner för att se nya meddelanden'
|
||||
'Online': 'Online'
|
||||
'Offline': 'Offline'
|
||||
'Connecting': 'Ansluter'
|
||||
'Connection re-established': 'Anslutningen återupprättas'
|
||||
'Today': 'I dag'
|
||||
'Send': 'Skicka'
|
||||
'Chat closed by %s': 'Chatt stängd av %s'
|
||||
'Compose your message...': 'Skriv ditt meddelande...'
|
||||
'All colleagues are busy.': 'Alla kollegor är upptagna.'
|
||||
'You are on waiting list position <strong>%s</strong>.': 'Du är på väntelistan som position <strong>%s</strong>.'
|
||||
'Start new conversation': 'Starta ny konversation'
|
||||
'Since you didn\'t respond in the last %s minutes your conversation with <strong>%s</strong> got closed.': 'Eftersom du inte svarat inom %s minuterna i din konversation med <strong>%s</strong> så stängdes chatten.'
|
||||
'Since you didn\'t respond in the last %s minutes your conversation got closed.': 'Då du inte svarat inom de senaste %s minuterna så avslutades din chatt.'
|
||||
'We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you!': 'Vi är ledsna, det tar längre tid som förväntat att få en ledig plats. Försök igen senare eller skicka ett e-postmeddelande till oss. Tack!'
|
||||
sessionId: undefined
|
||||
scrolledToBottom: true
|
||||
scrollSnapTolerance: 10
|
||||
|
||||
@@ -3306,3 +3306,9 @@ test('App.Utils.joinUrlComponents()', function() {
|
||||
// expect @joinUrlComponents() to filter them out of the results before joining the rest with slashes
|
||||
equal(App.Utils.joinUrlComponents('foo', undefined, 'bar', null, 'baz'), 'foo/bar/baz', 'with a list including null or undefined')
|
||||
});
|
||||
|
||||
test('App.Utils.signatureIdentifyByHtmlHelper()', function() {
|
||||
result = App.Utils.signatureIdentifyByHtmlHelper("<script>alert('fish2');</script><blockquote></blockquote>")
|
||||
|
||||
equal(result, "<script>alert('fish2');</script><span class=\"js-signatureMarker\"></span><blockquote></blockquote>", 'signatureIdentifyByHtmlHelper does not reactivate alert')
|
||||
});
|
||||
|
||||
@@ -15,5 +15,15 @@ RSpec.describe Issue2460FixCorruptedTwitterIds, type: :db_migration do
|
||||
.to change { twitter_channel.reload.options[:user][:id] }
|
||||
.to(twitter_api_user_id.to_s)
|
||||
end
|
||||
|
||||
context 'with invalid credentials stored' do
|
||||
|
||||
before { allow(Twitter::REST::Client).to receive(:new).and_raise(Twitter::Error::Unauthorized.new('Could not authenticate you.')) }
|
||||
|
||||
it 'skips the Channel' do
|
||||
expect { migrate }
|
||||
.not_to change { twitter_channel.reload.options[:user][:id] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
9
spec/factories/chat.rb
Normal file
9
spec/factories/chat.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
FactoryBot.define do
|
||||
factory :chat do
|
||||
sequence(:name) { |n| "Chat #{n}" }
|
||||
max_queue { 5 }
|
||||
active { true }
|
||||
created_by_id { 1 }
|
||||
updated_by_id { 1 }
|
||||
end
|
||||
end
|
||||
18
spec/models/chat_spec.rb
Normal file
18
spec/models/chat_spec.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Chat, type: :model do
|
||||
|
||||
describe 'website whitelisting' do
|
||||
let(:chat) { create(:chat, whitelisted_websites: 'zammad.org') }
|
||||
|
||||
it 'detects whitelisted website' do
|
||||
result = chat.website_whitelisted?('https://www.zammad.org')
|
||||
expect(result).to be true
|
||||
end
|
||||
|
||||
it 'detects non-whitelisted website' do
|
||||
result = chat.website_whitelisted?('https://www.zammad.com')
|
||||
expect(result).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -54,14 +54,14 @@ RSpec.describe ObjectManager::Attribute, type: :model do
|
||||
it 'rejects ActiveRecord reserved word "attribute"' do
|
||||
expect do
|
||||
described_class.add attributes_for :object_manager_attribute_text, name: 'attribute'
|
||||
end.to raise_error 'attribute is a reserved word, please choose a different one'
|
||||
end.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Name attribute is a reserved word! (2)')
|
||||
end
|
||||
|
||||
%w[destroy true false integer select drop create alter index table varchar blob date datetime timestamp url icon initials avatar permission validate subscribe unsubscribe translate search _type _doc _id id].each do |reserved_word|
|
||||
it "rejects Zammad reserved word '#{reserved_word}'" do
|
||||
expect do
|
||||
described_class.add attributes_for :object_manager_attribute_text, name: reserved_word
|
||||
end.to raise_error "#{reserved_word} is a reserved word, please choose a different one"
|
||||
end.to raise_error(ActiveRecord::RecordInvalid, /is a reserved word! \(1\)/)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -69,7 +69,7 @@ RSpec.describe ObjectManager::Attribute, type: :model do
|
||||
it "rejects word '#{reserved_word}' which is used for database references" do
|
||||
expect do
|
||||
described_class.add attributes_for :object_manager_attribute_text, name: reserved_word
|
||||
end.to raise_error "Name can't get used, *_id and *_ids are not allowed"
|
||||
end.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name can't get used because *_id and *_ids are not allowed")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ RSpec.describe 'Api Auth', type: :request do
|
||||
get '/api/v1/sessions', params: {}, as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.header['Access-Control-Allow-Origin']).to eq('*')
|
||||
expect(response.header['Cache-Control']).to match(/no-cache, no-store/)
|
||||
expect(response.header['Pragma']).to eq('no-cache')
|
||||
expect(response.header['Expires']).to eq('-1')
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response).to be_truthy
|
||||
end
|
||||
@@ -57,6 +60,9 @@ RSpec.describe 'Api Auth', type: :request do
|
||||
get '/api/v1/tickets', params: {}, as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.header['Access-Control-Allow-Origin']).to eq('*')
|
||||
expect(response.header['Cache-Control']).to match(/no-cache, no-store/)
|
||||
expect(response.header['Pragma']).to eq('no-cache')
|
||||
expect(response.header['Expires']).to eq('-1')
|
||||
expect(json_response).to be_a_kind_of(Array)
|
||||
expect(json_response).to be_truthy
|
||||
end
|
||||
@@ -75,6 +81,9 @@ RSpec.describe 'Api Auth', type: :request do
|
||||
get '/api/v1/tickets', params: {}, as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.header['Access-Control-Allow-Origin']).to eq('*')
|
||||
expect(response.header['Cache-Control']).to match(/no-cache, no-store/)
|
||||
expect(response.header['Pragma']).to eq('no-cache')
|
||||
expect(response.header['Expires']).to eq('-1')
|
||||
expect(json_response).to be_a_kind_of(Array)
|
||||
expect(json_response).to be_truthy
|
||||
end
|
||||
@@ -104,6 +113,9 @@ RSpec.describe 'Api Auth', type: :request do
|
||||
get '/api/v1/sessions', params: {}, as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.header['Access-Control-Allow-Origin']).to eq('*')
|
||||
expect(response.header['Cache-Control']).to match(/no-cache, no-store/)
|
||||
expect(response.header['Pragma']).to eq('no-cache')
|
||||
expect(response.header['Expires']).to eq('-1')
|
||||
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response).to be_truthy
|
||||
@@ -251,6 +263,9 @@ RSpec.describe 'Api Auth', type: :request do
|
||||
get '/api/v1/tickets', params: {}, as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.header['Access-Control-Allow-Origin']).to eq('*')
|
||||
expect(response.header['Cache-Control']).to match(/no-cache, no-store/)
|
||||
expect(response.header['Pragma']).to eq('no-cache')
|
||||
expect(response.header['Expires']).to eq('-1')
|
||||
expect(json_response).to be_a_kind_of(Array)
|
||||
expect(json_response).to be_truthy
|
||||
|
||||
@@ -286,6 +301,9 @@ RSpec.describe 'Api Auth', type: :request do
|
||||
Setting.set('api_token_access', true)
|
||||
get '/api/v1/tickets', params: {}, as: :json
|
||||
expect(response.header['Access-Control-Allow-Origin']).to eq('*')
|
||||
expect(response.header['Cache-Control']).to match(/no-cache, no-store/)
|
||||
expect(response.header['Pragma']).to eq('no-cache')
|
||||
expect(response.header['Expires']).to eq('-1')
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json_response).to be_a_kind_of(Array)
|
||||
expect(json_response).to be_truthy
|
||||
@@ -370,6 +388,9 @@ RSpec.describe 'Api Auth', type: :request do
|
||||
get '/api/v1/tickets', params: {}, as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.header['Access-Control-Allow-Origin']).to eq('*')
|
||||
expect(response.header['Cache-Control']).to match(/no-cache, no-store/)
|
||||
expect(response.header['Pragma']).to eq('no-cache')
|
||||
expect(response.header['Expires']).to eq('-1')
|
||||
expect(json_response).to be_a_kind_of(Array)
|
||||
expect(json_response).to be_truthy
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Basics', type: :request do
|
||||
|
||||
describe 'request handling' do
|
||||
|
||||
it 'does json requests' do
|
||||
|
||||
# 404
|
||||
get '/not_existing_url', as: :json
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response['error']).to eq('No route matches [GET] /not_existing_url')
|
||||
|
||||
# 401
|
||||
get '/api/v1/organizations', as: :json
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response['error']).to eq('authentication failed')
|
||||
|
||||
# 422
|
||||
get '/tests/unprocessable_entity', as: :json
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response['error']).to eq('some error message')
|
||||
|
||||
# 401
|
||||
get '/tests/not_authorized', as: :json
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response['error']).to eq('some error message')
|
||||
|
||||
# 401
|
||||
get '/tests/ar_not_found', as: :json
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response['error']).to eq('some error message')
|
||||
|
||||
# 500
|
||||
get '/tests/standard_error', as: :json
|
||||
expect(response).to have_http_status(:internal_server_error)
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response['error']).to eq('some error message')
|
||||
|
||||
# 422
|
||||
get '/tests/argument_error', as: :json
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response['error']).to eq('some error message')
|
||||
end
|
||||
|
||||
it 'does html requests' do
|
||||
|
||||
# 404
|
||||
get '/not_existing_url'
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(response.body).to match(/<html/)
|
||||
expect(response.body).to match(%r{<title>404: Not Found</title>})
|
||||
expect(response.body).to match(%r{<h1>404: Requested resource was not found</h1>})
|
||||
expect(response.body).to match(%r{No route matches \[GET\] /not_existing_url})
|
||||
|
||||
# 401
|
||||
get '/api/v1/organizations'
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
expect(response.body).to match(/<html/)
|
||||
expect(response.body).to match(%r{<title>401: Unauthorized</title>})
|
||||
expect(response.body).to match(%r{<h1>401: Unauthorized</h1>})
|
||||
expect(response.body).to match(/authentication failed/)
|
||||
|
||||
# 422
|
||||
get '/tests/unprocessable_entity'
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(response.body).to match(/<html/)
|
||||
expect(response.body).to match(%r{<title>422: Unprocessable Entity</title>})
|
||||
expect(response.body).to match(%r{<h1>422: The change you wanted was rejected.</h1>})
|
||||
expect(response.body).to match(/some error message/)
|
||||
|
||||
# 401
|
||||
get '/tests/not_authorized'
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
expect(response.body).to match(/<html/)
|
||||
expect(response.body).to match(%r{<title>401: Unauthorized</title>})
|
||||
expect(response.body).to match(%r{<h1>401: Unauthorized</h1>})
|
||||
expect(response.body).to match(/some error message/)
|
||||
|
||||
# 401
|
||||
get '/tests/ar_not_found'
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(response.body).to match(/<html/)
|
||||
expect(response.body).to match(%r{<title>404: Not Found</title>})
|
||||
expect(response.body).to match(%r{<h1>404: Requested resource was not found</h1>})
|
||||
expect(response.body).to match(/some error message/)
|
||||
|
||||
# 500
|
||||
get '/tests/standard_error'
|
||||
expect(response).to have_http_status(:internal_server_error)
|
||||
expect(response.body).to match(/<html/)
|
||||
expect(response.body).to match(%r{<title>500: Something went wrong</title>})
|
||||
expect(response.body).to match(%r{<h1>500: We're sorry, but something went wrong.</h1>})
|
||||
expect(response.body).to match(/some error message/)
|
||||
|
||||
# 422
|
||||
get '/tests/argument_error'
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(response.body).to match(/<html/)
|
||||
expect(response.body).to match(%r{<title>422: Unprocessable Entity</title>})
|
||||
expect(response.body).to match(%r{<h1>422: The change you wanted was rejected.</h1>})
|
||||
expect(response.body).to match(/some error message/)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
25
spec/requests/cors_preflight_check_spec.rb
Normal file
25
spec/requests/cors_preflight_check_spec.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'CORS Preflight Check', type: :request do
|
||||
|
||||
shared_examples 'empty response' do
|
||||
it { expect(response).to have_http_status(:ok) }
|
||||
it { expect(response.body).to be_empty }
|
||||
end
|
||||
|
||||
context 'valid route' do
|
||||
before do
|
||||
process :options, '/'
|
||||
end
|
||||
|
||||
include_examples 'empty response'
|
||||
end
|
||||
|
||||
context 'invalid route' do
|
||||
before do
|
||||
process :options, '/this_is_an_invalid_route'
|
||||
end
|
||||
|
||||
include_examples 'empty response'
|
||||
end
|
||||
end
|
||||
140
spec/requests/error_spec.rb
Normal file
140
spec/requests/error_spec.rb
Normal file
@@ -0,0 +1,140 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Error handling', type: :request do
|
||||
|
||||
shared_examples 'JSON response format' do
|
||||
let(:as) { :json }
|
||||
|
||||
it { expect(response).to have_http_status(http_status) }
|
||||
it { expect(json_response).to be_a_kind_of(Hash) }
|
||||
it do
|
||||
# There is a special case where we mask technical errors and return
|
||||
# a random error code that can be easily found in the logs by an
|
||||
# administrator. However, this makes it hard to check for the exact error
|
||||
# message. Therefore we only check for the substring in this particular case
|
||||
if message == 'Please contact your administrator'
|
||||
expect(json_response['error']).to include(message)
|
||||
else
|
||||
expect(json_response['error']).to eq(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'HTML response format' do
|
||||
let(:as) { :html }
|
||||
|
||||
it { expect(response).to have_http_status(http_status) }
|
||||
it { expect(response.body).to include('<html') }
|
||||
it { expect(response.body).to include("<title>#{title}</title>") }
|
||||
it { expect(response.body).to include("<h1>#{headline}</h1>") }
|
||||
it { expect(response.body).to include(message) }
|
||||
end
|
||||
|
||||
context 'error with confidential message is raised' do
|
||||
|
||||
let(:admin_user) { create(:admin_user, groups: Group.all) }
|
||||
let!(:ticket) { create(:ticket) }
|
||||
let(:invalid_group_id) { 99_999 }
|
||||
let(:message) { 'Please contact your administrator' }
|
||||
let(:http_status) { :unprocessable_entity }
|
||||
|
||||
before do
|
||||
# fake production ENV to enable error hiding
|
||||
env = double(
|
||||
production?: true,
|
||||
test?: false,
|
||||
development?: false
|
||||
)
|
||||
allow(::Rails).to receive(:env).and_return(env)
|
||||
|
||||
authenticated_as(admin_user)
|
||||
put "/api/v1/tickets/#{ticket.id}?all=true", params: { group_id: invalid_group_id }, as: as
|
||||
end
|
||||
|
||||
context 'requesting JSON' do
|
||||
include_examples 'JSON response format'
|
||||
end
|
||||
|
||||
context 'requesting HTML' do
|
||||
let(:title) { '422: Unprocessable Entity' }
|
||||
let(:headline) { '422: The change you wanted was rejected.' }
|
||||
|
||||
include_examples 'HTML response format'
|
||||
end
|
||||
end
|
||||
|
||||
context 'URL route does not exist' do
|
||||
|
||||
before do
|
||||
get '/not_existing_url', as: as
|
||||
end
|
||||
|
||||
let(:message) { 'No route matches [GET] /not_existing_url' }
|
||||
let(:http_status) { :not_found }
|
||||
|
||||
context 'requesting JSON' do
|
||||
include_examples 'JSON response format'
|
||||
end
|
||||
|
||||
context 'requesting HTML' do
|
||||
let(:title) { '404: Not Found' }
|
||||
let(:headline) { '404: Requested resource was not found' }
|
||||
|
||||
include_examples 'HTML response format'
|
||||
end
|
||||
end
|
||||
|
||||
context 'request is not authenticated' do
|
||||
|
||||
before do
|
||||
get '/api/v1/organizations', as: as
|
||||
end
|
||||
|
||||
let(:message) { 'authentication failed' }
|
||||
let(:http_status) { :unauthorized }
|
||||
|
||||
context 'requesting JSON' do
|
||||
include_examples 'JSON response format'
|
||||
end
|
||||
|
||||
context 'requesting HTML' do
|
||||
let(:title) { '401: Unauthorized' }
|
||||
let(:headline) { '401: Unauthorized' }
|
||||
|
||||
include_examples 'HTML response format'
|
||||
end
|
||||
end
|
||||
|
||||
context 'exception is raised' do
|
||||
|
||||
before do
|
||||
get '/tests/raised_exception', params: { exception: exception.name, message: message }, as: as
|
||||
end
|
||||
|
||||
shared_examples 'handles exception' do |exception, http_status, title, headline|
|
||||
|
||||
context "#{exception} is raised" do
|
||||
let(:exception) { exception }
|
||||
let(:http_status) { http_status }
|
||||
let(:message) { 'some error message' }
|
||||
|
||||
context 'requesting JSON' do
|
||||
include_examples 'JSON response format'
|
||||
end
|
||||
|
||||
context 'requesting HTML' do
|
||||
let(:title) { title }
|
||||
let(:headline) { headline }
|
||||
|
||||
include_examples 'HTML response format'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
include_examples 'handles exception', ActiveRecord::RecordNotFound, :not_found, '404: Not Found', '404: Requested resource was not found'
|
||||
include_examples 'handles exception', Exceptions::NotAuthorized, :unauthorized, '401: Unauthorized', '401: Unauthorized'
|
||||
include_examples 'handles exception', Exceptions::UnprocessableEntity, :unprocessable_entity, '422: Unprocessable Entity', '422: The change you wanted was rejected.'
|
||||
include_examples 'handles exception', ArgumentError, :unprocessable_entity, '422: Unprocessable Entity', '422: The change you wanted was rejected.'
|
||||
include_examples 'handles exception', StandardError, :internal_server_error, '500: Something went wrong', "500: We're sorry, but something went wrong."
|
||||
end
|
||||
end
|
||||
@@ -522,6 +522,10 @@ RSpec.describe 'Monitoring', type: :request do
|
||||
prev_es_config = Setting.get('es_url')
|
||||
Setting.set('es_url', 'http://127.0.0.1:92001')
|
||||
|
||||
# delete all background jobs created while seeding
|
||||
# to have a clean state for checking for failed ones
|
||||
Delayed::Job.destroy_all
|
||||
|
||||
# add a new object
|
||||
object = create(:object_manager_attribute_text)
|
||||
|
||||
|
||||
@@ -2,6 +2,33 @@ require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Sessions endpoints', type: :request do
|
||||
|
||||
describe 'GET /signshow' do
|
||||
|
||||
context 'user logged in' do
|
||||
|
||||
subject(:user) { create(:agent_user, password: password) }
|
||||
|
||||
let(:password) { SecureRandom.urlsafe_base64(20) }
|
||||
let(:fingerprint) { SecureRandom.urlsafe_base64(40) }
|
||||
|
||||
before do
|
||||
params = {
|
||||
fingerprint: fingerprint,
|
||||
username: user.login,
|
||||
password: password
|
||||
}
|
||||
post '/api/v1/signin', params: params, as: :json
|
||||
end
|
||||
|
||||
it 'leaks no sensitive data' do
|
||||
params = { fingerprint: fingerprint }
|
||||
get '/api/v1/signshow', params: params, as: :json
|
||||
|
||||
expect(json_response['session']).not_to include('password')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /auth/sso (single sign-on)' do
|
||||
context 'with invalid user login' do
|
||||
let(:login) { User.pluck(:login).max.next }
|
||||
|
||||
@@ -2141,4 +2141,59 @@ RSpec.describe 'Ticket', type: :request do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/ticket_customer' do
|
||||
|
||||
subject(:ticket) { create(:ticket, customer: customer_authorized) }
|
||||
|
||||
let(:organization_authorized) { create(:organization) }
|
||||
let(:customer_authorized) { create(:customer_user, organization: organization_authorized) }
|
||||
|
||||
let(:organization_unauthorized) { create(:organization) }
|
||||
let(:customer_unauthorized) { create(:customer_user, organization: organization_unauthorized) }
|
||||
|
||||
let(:agent) { create(:agent_user, groups: [ticket.group]) }
|
||||
|
||||
describe 'listing information' do
|
||||
|
||||
before do
|
||||
ticket
|
||||
end
|
||||
|
||||
shared_examples 'has access' do
|
||||
it 'succeeds' do
|
||||
get '/api/v1/ticket_customer',
|
||||
params: { customer_id: customer_authorized.id },
|
||||
as: :json
|
||||
|
||||
expect(json_response['ticket_ids_open']).to include(ticket.id)
|
||||
expect(json_response['ticket_ids_closed']).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'has no access' do
|
||||
it 'fails' do
|
||||
get '/api/v1/ticket_customer',
|
||||
params: { customer_id: customer_authorized.id },
|
||||
as: :json
|
||||
|
||||
expect(json_response['ticket_ids_open']).to be_blank
|
||||
expect(json_response['ticket_ids_closed']).to be_blank
|
||||
expect(json_response['assets']).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
context 'as agent', authenticated_as: -> { agent } do
|
||||
include_examples 'has access'
|
||||
end
|
||||
|
||||
context 'as authorized customer', authenticated_as: -> { customer_authorized } do
|
||||
include_examples 'has access'
|
||||
end
|
||||
|
||||
context 'as unauthorized customer', authenticated_as: -> { customer_unauthorized } do
|
||||
include_examples 'has no access'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -126,7 +126,50 @@ RSpec.describe 'Ticket zoom', type: :system do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'when ticket has an attachment' do
|
||||
|
||||
let(:group) { Group.find_by(name: 'Users') }
|
||||
let(:ticket) { create(:ticket, group: group) }
|
||||
let(:article) { create(:ticket_article, ticket: ticket) }
|
||||
let(:attachment_name) { 'some_file.txt' }
|
||||
|
||||
before do
|
||||
Store.add(
|
||||
object: 'Ticket::Article',
|
||||
o_id: article.id,
|
||||
data: 'some content',
|
||||
filename: attachment_name,
|
||||
preferences: {
|
||||
'Content-Type' => 'text/plain',
|
||||
},
|
||||
created_by_id: 1,
|
||||
)
|
||||
end
|
||||
|
||||
context 'article was already forwarded once' do
|
||||
before do
|
||||
visit "#ticket/zoom/#{ticket.id}"
|
||||
|
||||
within(:active_content) do
|
||||
find('a[data-type=emailForward]').click
|
||||
|
||||
click('.js-reset')
|
||||
have_no_css('.js-reset')
|
||||
end
|
||||
end
|
||||
|
||||
it 'adds attachments when forwarding multiple times' do
|
||||
|
||||
within(:active_content) do
|
||||
find('a[data-type=emailForward]').click
|
||||
end
|
||||
|
||||
within('.js-writeArea') do
|
||||
expect(page).to have_text attachment_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -208,9 +208,11 @@ class SignupPasswordChangeAndResetTest < TestCase
|
||||
click(css: '.content .btn--primary')
|
||||
watch_for(
|
||||
css: 'body',
|
||||
value: 'address invalid',
|
||||
value: 'sent password reset instructions',
|
||||
)
|
||||
|
||||
click(css: '.content .btn--primary')
|
||||
|
||||
set(
|
||||
css: 'input[name="username"]',
|
||||
value: signup_user_email,
|
||||
|
||||
@@ -4,9 +4,9 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
|
||||
test 'a mx_records' do
|
||||
|
||||
domain = 'znuny.com'
|
||||
domain = 'zammad.com'
|
||||
mx_domains = EmailHelper.mx_records(domain)
|
||||
assert_equal('arber.znuny.com', mx_domains[0])
|
||||
assert_equal('mx2.zammad.com', mx_domains[0])
|
||||
|
||||
end
|
||||
|
||||
@@ -48,20 +48,20 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
|
||||
test 'provider_inbound_mx' do
|
||||
|
||||
email = 'linus@znuny.com'
|
||||
email = 'linus@zammad.com'
|
||||
password = 'some_pw'
|
||||
user, domain = EmailHelper.parse_email(email)
|
||||
mx_domains = EmailHelper.mx_records(domain)
|
||||
map = EmailHelper.provider_inbound_mx(user, email, password, mx_domains)
|
||||
|
||||
assert_equal('imap', map[0][:adapter])
|
||||
assert_equal('arber.znuny.com', map[0][:options][:host])
|
||||
assert_equal('mx2.zammad.com', map[0][:options][:host])
|
||||
assert_equal(993, map[0][:options][:port])
|
||||
assert_equal(user, map[0][:options][:user])
|
||||
assert_equal(password, map[0][:options][:password])
|
||||
|
||||
assert_equal('imap', map[1][:adapter])
|
||||
assert_equal('arber.znuny.com', map[1][:options][:host])
|
||||
assert_equal('mx2.zammad.com', map[1][:options][:host])
|
||||
assert_equal(993, map[1][:options][:port])
|
||||
assert_equal(email, map[1][:options][:user])
|
||||
assert_equal(password, map[1][:options][:password])
|
||||
@@ -70,19 +70,19 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
|
||||
test 'provider_inbound_guess' do
|
||||
|
||||
email = 'linus@znuny.com'
|
||||
email = 'linus@zammad.com'
|
||||
password = 'some_pw'
|
||||
user, domain = EmailHelper.parse_email(email)
|
||||
map = EmailHelper.provider_inbound_guess(user, email, password, domain)
|
||||
|
||||
assert_equal('imap', map[0][:adapter])
|
||||
assert_equal('mail.znuny.com', map[0][:options][:host])
|
||||
assert_equal('mail.zammad.com', map[0][:options][:host])
|
||||
assert_equal(993, map[0][:options][:port])
|
||||
assert_equal(user, map[0][:options][:user])
|
||||
assert_equal(password, map[0][:options][:password])
|
||||
|
||||
assert_equal('imap', map[1][:adapter])
|
||||
assert_equal('mail.znuny.com', map[1][:options][:host])
|
||||
assert_equal('mail.zammad.com', map[1][:options][:host])
|
||||
assert_equal(993, map[1][:options][:port])
|
||||
assert_equal(email, map[1][:options][:user])
|
||||
assert_equal(password, map[1][:options][:password])
|
||||
@@ -91,21 +91,21 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
|
||||
test 'provider_outbound_mx' do
|
||||
|
||||
email = 'linus@znuny.com'
|
||||
email = 'linus@zammad.com'
|
||||
password = 'some_pw'
|
||||
user, domain = EmailHelper.parse_email(email)
|
||||
mx_domains = EmailHelper.mx_records(domain)
|
||||
map = EmailHelper.provider_outbound_mx(user, email, password, mx_domains)
|
||||
|
||||
assert_equal('smtp', map[0][:adapter])
|
||||
assert_equal('arber.znuny.com', map[0][:options][:host])
|
||||
assert_equal('mx2.zammad.com', map[0][:options][:host])
|
||||
assert_equal(25, map[0][:options][:port])
|
||||
assert_equal(true, map[0][:options][:start_tls])
|
||||
assert_equal(user, map[0][:options][:user])
|
||||
assert_equal(password, map[0][:options][:password])
|
||||
|
||||
assert_equal('smtp', map[1][:adapter])
|
||||
assert_equal('arber.znuny.com', map[1][:options][:host])
|
||||
assert_equal('mx2.zammad.com', map[1][:options][:host])
|
||||
assert_equal(25, map[1][:options][:port])
|
||||
assert_equal(true, map[1][:options][:start_tls])
|
||||
assert_equal(email, map[1][:options][:user])
|
||||
@@ -115,20 +115,20 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
|
||||
test 'provider_outbound_guess' do
|
||||
|
||||
email = 'linus@znuny.com'
|
||||
email = 'linus@zammad.com'
|
||||
password = 'some_pw'
|
||||
user, domain = EmailHelper.parse_email(email)
|
||||
map = EmailHelper.provider_outbound_guess(user, email, password, domain)
|
||||
|
||||
assert_equal('smtp', map[0][:adapter])
|
||||
assert_equal('mail.znuny.com', map[0][:options][:host])
|
||||
assert_equal('mail.zammad.com', map[0][:options][:host])
|
||||
assert_equal(25, map[0][:options][:port])
|
||||
assert_equal(true, map[0][:options][:start_tls])
|
||||
assert_equal(user, map[0][:options][:user])
|
||||
assert_equal(password, map[0][:options][:password])
|
||||
|
||||
assert_equal('smtp', map[1][:adapter])
|
||||
assert_equal('mail.znuny.com', map[1][:options][:host])
|
||||
assert_equal('mail.zammad.com', map[1][:options][:host])
|
||||
assert_equal(25, map[1][:options][:port])
|
||||
assert_equal(true, map[1][:options][:start_tls])
|
||||
assert_equal(email, map[1][:options][:user])
|
||||
@@ -236,7 +236,7 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
result = EmailHelper::Probe.inbound(
|
||||
adapter: 'imap',
|
||||
options: {
|
||||
host: 'arber.znuny.com',
|
||||
host: 'mx2.zammad.com',
|
||||
port: 993,
|
||||
ssl: true,
|
||||
user: 'some@example.com',
|
||||
@@ -245,23 +245,22 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
)
|
||||
assert_equal('invalid', result[:result])
|
||||
assert_equal('Authentication failed!', result[:message_human])
|
||||
assert_equal('arber.znuny.com', result[:settings][:options][:host])
|
||||
assert_equal('mx2.zammad.com', result[:settings][:options][:host])
|
||||
|
||||
# realtest - test I
|
||||
if !ENV['EMAILHELPER_MAILBOX_1']
|
||||
raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'"
|
||||
raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@zammad.com:somepass'"
|
||||
end
|
||||
|
||||
mailbox_user = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0]
|
||||
mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1]
|
||||
user, _domain = EmailHelper.parse_email(mailbox_user)
|
||||
result = EmailHelper::Probe.inbound(
|
||||
adapter: 'imap',
|
||||
options: {
|
||||
host: 'arber.znuny.com',
|
||||
host: 'mx2.zammad.com',
|
||||
port: 993,
|
||||
ssl: true,
|
||||
user: user,
|
||||
user: mailbox_user,
|
||||
password: mailbox_password,
|
||||
}
|
||||
)
|
||||
@@ -388,36 +387,33 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
{
|
||||
adapter: 'smtp',
|
||||
options: {
|
||||
host: 'arber.znuny.com',
|
||||
port: 25,
|
||||
start_tls: true,
|
||||
user: 'some@example.com',
|
||||
password: 'password',
|
||||
host: 'mx2.zammad.com',
|
||||
port: 587,
|
||||
user: 'some@example.com',
|
||||
password: 'password',
|
||||
}
|
||||
},
|
||||
'some@example.com',
|
||||
)
|
||||
assert_equal('invalid', result[:result])
|
||||
assert_equal('Authentication failed!', result[:message_human])
|
||||
assert_equal('arber.znuny.com', result[:settings][:options][:host])
|
||||
assert_equal('mx2.zammad.com', result[:settings][:options][:host])
|
||||
|
||||
# realtest - test I
|
||||
if !ENV['EMAILHELPER_MAILBOX_1']
|
||||
raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'"
|
||||
raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@zammad.com:somepass'"
|
||||
end
|
||||
|
||||
mailbox_user = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0]
|
||||
mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1]
|
||||
user, _domain = EmailHelper.parse_email(mailbox_user)
|
||||
result = EmailHelper::Probe.outbound(
|
||||
{
|
||||
adapter: 'smtp',
|
||||
options: {
|
||||
host: 'arber.znuny.com',
|
||||
port: 25,
|
||||
start_tls: true,
|
||||
user: user,
|
||||
password: mailbox_password,
|
||||
host: 'mx2.zammad.com',
|
||||
port: 587,
|
||||
user: mailbox_user,
|
||||
password: mailbox_password,
|
||||
}
|
||||
},
|
||||
mailbox_user,
|
||||
@@ -436,7 +432,7 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
|
||||
# realtest - test I, with imap
|
||||
if !ENV['EMAILHELPER_MAILBOX_1']
|
||||
raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'"
|
||||
raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@zammad.com:somepass'"
|
||||
end
|
||||
|
||||
mailbox_user = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0]
|
||||
@@ -447,8 +443,8 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
password: mailbox_password,
|
||||
)
|
||||
assert_equal('ok', result[:result])
|
||||
assert_equal('arber.znuny.com', result[:setting][:inbound][:options][:host])
|
||||
assert_equal('arber.znuny.com', result[:setting][:outbound][:options][:host])
|
||||
assert_equal('mx2.zammad.com', result[:setting][:inbound][:options][:host])
|
||||
assert_equal('mx2.zammad.com', result[:setting][:outbound][:options][:host])
|
||||
|
||||
# realtest - test II, gmail with only pop3
|
||||
if !ENV['EMAILHELPER_MAILBOX_2']
|
||||
@@ -473,31 +469,29 @@ class EmailHelperTest < ActiveSupport::TestCase
|
||||
|
||||
# realtest - test I, with imap
|
||||
if !ENV['EMAILHELPER_MAILBOX_1']
|
||||
raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'"
|
||||
raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@zammad.com:somepass'"
|
||||
end
|
||||
|
||||
mailbox_user = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0]
|
||||
mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1]
|
||||
user, _domain = EmailHelper.parse_email(mailbox_user)
|
||||
result = EmailHelper::Verify.email(
|
||||
inbound: {
|
||||
adapter: 'imap',
|
||||
options: {
|
||||
host: 'arber.znuny.com',
|
||||
host: 'mx2.zammad.com',
|
||||
port: 993,
|
||||
ssl: true,
|
||||
user: user,
|
||||
user: mailbox_user,
|
||||
password: mailbox_password,
|
||||
},
|
||||
},
|
||||
outbound: {
|
||||
adapter: 'smtp',
|
||||
options: {
|
||||
host: 'arber.znuny.com',
|
||||
port: 25,
|
||||
start_tls: true,
|
||||
user: user,
|
||||
password: mailbox_password,
|
||||
host: 'mx2.zammad.com',
|
||||
port: 587,
|
||||
user: mailbox_user,
|
||||
password: mailbox_password,
|
||||
},
|
||||
},
|
||||
sender: mailbox_user,
|
||||
|
||||
@@ -73,7 +73,7 @@ class ObjectManagerTest < ActiveSupport::TestCase
|
||||
assert_not(attribute1)
|
||||
|
||||
# create invalid attributes
|
||||
assert_raises(RuntimeError) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
ObjectManager::Attribute.add(
|
||||
object: 'Ticket',
|
||||
name: 'test2_id',
|
||||
@@ -91,7 +91,7 @@ class ObjectManagerTest < ActiveSupport::TestCase
|
||||
updated_by_id: 1,
|
||||
)
|
||||
end
|
||||
assert_raises(RuntimeError) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
ObjectManager::Attribute.add(
|
||||
object: 'Ticket',
|
||||
name: 'test3_ids',
|
||||
@@ -286,7 +286,7 @@ class ObjectManagerTest < ActiveSupport::TestCase
|
||||
)
|
||||
assert_equal(false, ObjectManager::Attribute.pending_migration?)
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
ObjectManager::Attribute.add(
|
||||
object: 'Ticket',
|
||||
name: 'test13|',
|
||||
@@ -307,7 +307,7 @@ class ObjectManagerTest < ActiveSupport::TestCase
|
||||
end
|
||||
assert_equal(false, ObjectManager::Attribute.pending_migration?)
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
ObjectManager::Attribute.add(
|
||||
object: 'Ticket',
|
||||
name: 'test14!',
|
||||
@@ -328,7 +328,7 @@ class ObjectManagerTest < ActiveSupport::TestCase
|
||||
end
|
||||
assert_equal(false, ObjectManager::Attribute.pending_migration?)
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
ObjectManager::Attribute.add(
|
||||
object: 'Ticket',
|
||||
name: 'test15ä',
|
||||
@@ -370,7 +370,7 @@ class ObjectManagerTest < ActiveSupport::TestCase
|
||||
end
|
||||
assert_equal(false, ObjectManager::Attribute.pending_migration?)
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
ObjectManager::Attribute.add(
|
||||
object: 'Ticket',
|
||||
name: 'delete',
|
||||
@@ -391,7 +391,7 @@ class ObjectManagerTest < ActiveSupport::TestCase
|
||||
assert_equal(false, ObjectManager::Attribute.pending_migration?)
|
||||
|
||||
attribute_count = ObjectManager::Attribute.count
|
||||
assert_raises(RuntimeError) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
ObjectManager::Attribute.add(
|
||||
object: 'Ticket',
|
||||
name: 'updated_at',
|
||||
@@ -413,7 +413,7 @@ class ObjectManagerTest < ActiveSupport::TestCase
|
||||
end
|
||||
assert_equal(attribute_count, ObjectManager::Attribute.count)
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
assert_raises(ActiveRecord::RecordInvalid) do
|
||||
ObjectManager::Attribute.add(
|
||||
object: 'Ticket',
|
||||
name: 'updated_AT',
|
||||
|
||||
Reference in New Issue
Block a user