Compare commits
163 commits
antifascis
...
antifascis
Author | SHA1 | Date | |
---|---|---|---|
|
891987042f | ||
|
089e4c36d3 | ||
|
348002559f | ||
|
658952a5cd | ||
|
fba5cdc838 | ||
|
e5c56f1a1c | ||
|
d16b9b5ba4 | ||
|
ca2885dce0 | ||
|
4cb5a7776b | ||
|
91dc61bbaa | ||
|
30fd5d34da | ||
|
469d532b06 | ||
|
62f319b589 | ||
|
a33869cf29 | ||
|
3835bd3fdb | ||
|
aa4ade336e | ||
|
bb025db806 | ||
|
8fcb3fb52e | ||
|
7703c3efc6 | ||
|
f44a1a2bd4 | ||
|
141dc0cad2 | ||
|
879205d228 | ||
|
7ff7be804c | ||
|
bf5cb0a593 | ||
|
7ef5032e27 | ||
|
f20e784501 | ||
|
55765e29ed | ||
|
3413a2909c | ||
|
e3afa31402 | ||
|
7adc0a12d0 | ||
|
82041f427b | ||
|
9006138d65 | ||
|
3b18bb36dd | ||
|
58807875e9 | ||
|
a7bc54db37 | ||
|
5163888b70 | ||
|
9ed29355f0 | ||
|
152fb9aa05 | ||
|
f5c797719d | ||
|
49a541b024 | ||
|
6ff08abc3a | ||
|
4397a7be50 | ||
|
cf2d8dc820 | ||
|
23c5f43fca | ||
|
827c18b471 | ||
|
ecd29123c2 | ||
|
b82b7693cf | ||
|
b757f8e2c5 | ||
|
a8aea71a85 | ||
|
0e60fd5f62 | ||
|
140ebbb3ff | ||
|
7d56a6a975 | ||
|
82b61e4811 | ||
|
4905812382 | ||
|
9435da0a36 | ||
|
106f55b4c2 | ||
|
04d1459121 | ||
|
d2f24fc0fc | ||
|
913dcdd2ab | ||
|
f4116fb181 | ||
|
93c3eb1e20 | ||
|
28eca31145 | ||
|
8b16f483d6 | ||
|
10be8dabd3 | ||
|
29d99f1d11 | ||
|
c8a09f6ce8 | ||
|
47b72864be | ||
|
1ba497d6ac | ||
|
49162d32d0 | ||
|
fb5ad0862b | ||
|
59a7bce347 | ||
|
4d90a7b240 | ||
|
a9b57e5719 | ||
|
dc5ba3cbaa | ||
|
44703499e3 | ||
|
8053f2fc72 | ||
|
86ded71621 | ||
|
86671d5eaf | ||
|
dd92280833 | ||
|
cb3516e49d | ||
|
3e3e409562 | ||
|
ff6af07b9a | ||
|
1bd1cd669f | ||
|
c75141c2ff | ||
|
907062b7eb | ||
|
06af2b7936 | ||
|
e54158c0a8 | ||
|
49759f0be8 | ||
|
2c26e82354 | ||
|
3f1057c190 | ||
|
ad12ad4e01 | ||
|
ce5b9bd9de | ||
|
59cfc7c69a | ||
|
d9df6de234 | ||
|
4f94b01d56 | ||
|
678b5ada31 | ||
|
4ca7c25480 | ||
|
b9bb88e96d | ||
|
274e5a849f | ||
|
cb48cd2dcd | ||
|
6de1053d4b | ||
|
0f4c6ddda3 | ||
|
485196b013 | ||
|
ce1527f259 | ||
|
71f35b069e | ||
|
3ddb52c9b9 | ||
|
2346eaa005 | ||
|
d17e3c5b55 | ||
|
8762f96810 | ||
|
6281816d4d | ||
|
372bbb0751 | ||
|
fa8588855c | ||
|
905bdd512b | ||
|
fd99f097d5 | ||
|
880b2fd131 | ||
|
c4ccbc46ff | ||
|
d638f02a12 | ||
|
497b6aaeff | ||
|
4e05f0c121 | ||
|
f18c0c9571 | ||
|
d288b48b0a | ||
|
94011846d6 | ||
|
09fc94cf72 | ||
|
c34452f65e | ||
|
2b77711150 | ||
|
57945692cc | ||
|
dfb2abe380 | ||
|
07c6bdacfb | ||
|
ff03e664db | ||
|
c72006216e | ||
|
6efafd37ae | ||
|
35f45f690f | ||
|
23c8db7e58 | ||
|
8382a71813 | ||
|
9bb1cf6d00 | ||
|
2bba743dc3 | ||
|
d7b0c639c4 | ||
|
13668dfc8b | ||
|
1e70665d1a | ||
|
4b0d4f798f | ||
|
9ad7c43cd5 | ||
|
ab9fa3d23d | ||
|
5222f4ba6d | ||
|
9689d3d97e | ||
|
6a10308f29 | ||
|
ab3cede8ef | ||
|
fa3cb1925d | ||
|
a7def1d62d | ||
|
a3df6b8581 | ||
|
dd201db791 | ||
|
60c0b08d21 | ||
|
95571b70c9 | ||
|
678161be2e | ||
|
008053f730 | ||
|
ed1782fafc | ||
|
72f39d0d9d | ||
|
7a156c5d48 | ||
|
f31aeec8db | ||
|
f67cf3ab1e | ||
|
b125b3603e | ||
|
b1e8b3afae | ||
|
573c975bad | ||
|
25155eb6a3 |
366 changed files with 9939 additions and 5635 deletions
20
.coffeelint/rules/prevent_underscore_backport.coffee
Normal file
20
.coffeelint/rules/prevent_underscore_backport.coffee
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module.exports = class PreventUnderscoreBackport
|
||||||
|
|
||||||
|
rule:
|
||||||
|
name: 'prevent_underscore_backport'
|
||||||
|
level: 'error'
|
||||||
|
message: 'The method __(...) is not available in current stable'
|
||||||
|
description: '''
|
||||||
|
'''
|
||||||
|
|
||||||
|
constructor: ->
|
||||||
|
@callTokens = []
|
||||||
|
|
||||||
|
tokens: ['CALL_START']
|
||||||
|
|
||||||
|
lintToken: (token, tokenApi) ->
|
||||||
|
[type, tokenValue] = token
|
||||||
|
|
||||||
|
p = tokenApi.peek(-1)
|
||||||
|
if p[1] == '__'
|
||||||
|
return { }
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -84,6 +84,9 @@
|
||||||
# Eclipse
|
# Eclipse
|
||||||
/.project
|
/.project
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
/.vscode
|
||||||
|
|
||||||
# Byebug
|
# Byebug
|
||||||
/.byebug_history
|
/.byebug_history
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
default:
|
default:
|
||||||
image: registry.znuny.com/docker/zammad-ci:2.7.4
|
image: $CI_REGISTRY/docker/zammad-ci:2.7.4
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- local: '/.gitlab/ci/base.yml'
|
- local: '/.gitlab/ci/base.yml'
|
||||||
|
@ -55,7 +55,7 @@ cache:
|
||||||
# Initialize application env
|
# Initialize application env
|
||||||
before_script:
|
before_script:
|
||||||
- source /etc/profile.d/rvm.sh
|
- source /etc/profile.d/rvm.sh
|
||||||
- source /opt/rh/rh-nodejs12/enable
|
- source /opt/rh/rh-nodejs*/enable
|
||||||
- bundle install -j $(nproc) --path vendor
|
- bundle install -j $(nproc) --path vendor
|
||||||
- bundle exec ruby .gitlab/configure_environment.rb
|
- bundle exec ruby .gitlab/configure_environment.rb
|
||||||
- source .gitlab/environment.env
|
- source .gitlab/environment.env
|
||||||
|
|
|
@ -74,35 +74,35 @@
|
||||||
|
|
||||||
# DB Docker
|
# DB Docker
|
||||||
.docker_mysql: &docker_mysql
|
.docker_mysql: &docker_mysql
|
||||||
name: registry.znuny.com/docker/zammad-mysql:stable
|
name: $CI_REGISTRY/docker/zammad-mysql:stable
|
||||||
alias: mysql
|
alias: mysql
|
||||||
|
|
||||||
.docker_postgresql: &docker_postgresql
|
.docker_postgresql: &docker_postgresql
|
||||||
name: registry.znuny.com/docker/zammad-postgresql:stable
|
name: $CI_REGISTRY/docker/zammad-postgresql:stable
|
||||||
alias: postgresql
|
alias: postgresql
|
||||||
|
|
||||||
.docker_elasticsearch: &docker_elasticsearch
|
.docker_elasticsearch: &docker_elasticsearch
|
||||||
name: registry.znuny.com/docker/zammad-elasticsearch:$ELASTICSEARCH_TAG
|
name: $CI_REGISTRY/docker/zammad-elasticsearch:$ELASTICSEARCH_TAG
|
||||||
alias: elasticsearch
|
alias: elasticsearch
|
||||||
|
|
||||||
.docker_selenium_chrome: &docker_selenium_chrome
|
.docker_selenium_chrome: &docker_selenium_chrome
|
||||||
name: registry.znuny.com/docker/zammad-selenium-chrome:stable
|
name: $CI_REGISTRY/docker/zammad-selenium-chrome:stable
|
||||||
alias: selenium-chrome
|
alias: selenium-chrome
|
||||||
|
|
||||||
.docker_selenium_firefox: &docker_selenium_firefox
|
.docker_selenium_firefox: &docker_selenium_firefox
|
||||||
name: registry.znuny.com/docker/zammad-selenium-firefox:stable
|
name: $CI_REGISTRY/docker/zammad-selenium-firefox:stable
|
||||||
alias: selenium-firefox
|
alias: selenium-firefox
|
||||||
|
|
||||||
.docker_imap: &docker_imap
|
.docker_imap: &docker_imap
|
||||||
name: registry.znuny.com/docker/zammad-imap:stable
|
name: $CI_REGISTRY/docker/zammad-imap:stable
|
||||||
alias: mail
|
alias: mail
|
||||||
|
|
||||||
.docker_redis: &docker_redis
|
.docker_redis: &docker_redis
|
||||||
name: registry.znuny.com/docker/zammad-redis:stable
|
name: $CI_REGISTRY/docker/zammad-redis:stable
|
||||||
alias: redis
|
alias: redis
|
||||||
|
|
||||||
.docker_memcached: &docker_memcached
|
.docker_memcached: &docker_memcached
|
||||||
name: registry.znuny.com/docker/zammad-memcached:stable
|
name: $CI_REGISTRY/docker/zammad-memcached:stable
|
||||||
alias: memcached
|
alias: memcached
|
||||||
command: ["memcached", "-m", "256M"]
|
command: ["memcached", "-m", "256M"]
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ api_client_php:
|
||||||
script:
|
script:
|
||||||
- RAILS_ENV=test bundle exec rake db:create
|
- RAILS_ENV=test bundle exec rake db:create
|
||||||
- RAILS_ENV=test 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
|
- git clone https://github.com/zammad/zammad-api-client-php.git -b zammad-ci-5.0 # Use state with tests compatible to 5.0
|
||||||
- php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
- php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||||
- php composer-setup.php --install-dir=/usr/local/bin
|
- php composer-setup.php --install-dir=/usr/local/bin
|
||||||
- ln -s /usr/local/bin/composer.phar /usr/local/bin/composer
|
- ln -s /usr/local/bin/composer.phar /usr/local/bin/composer
|
||||||
|
|
|
@ -7,11 +7,11 @@ otrs_chrome:
|
||||||
IMPORT_OTRS_ENDPOINT: "http://zammad-ci-otrsimport-app/otrs/public.pl?Action=ZammadMigrator"
|
IMPORT_OTRS_ENDPOINT: "http://zammad-ci-otrsimport-app/otrs/public.pl?Action=ZammadMigrator"
|
||||||
TZ: "Europe/Berlin" # Required for the zammad-ci-otrsimport-app containers
|
TZ: "Europe/Berlin" # Required for the zammad-ci-otrsimport-app containers
|
||||||
services:
|
services:
|
||||||
- name: registry.znuny.com/docker/zammad-postgresql:stable
|
- name: $CI_REGISTRY/docker/zammad-postgresql:stable
|
||||||
alias: postgresql
|
alias: postgresql
|
||||||
- name: registry.znuny.com/docker/zammad-selenium-chrome:stable
|
- name: $CI_REGISTRY/docker/zammad-selenium-chrome:stable
|
||||||
alias: selenium-chrome
|
alias: selenium-chrome
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-db:otrs6
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-db:otrs6
|
||||||
alias: zammad-ci-otrsimport-db
|
alias: zammad-ci-otrsimport-db
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-app:otrs6
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-app:otrs6
|
||||||
alias: zammad-ci-otrsimport-app
|
alias: zammad-ci-otrsimport-app
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
- bundle exec rails test test/integration/elasticsearch_active_test.rb
|
- bundle exec rails test test/integration/elasticsearch_active_test.rb
|
||||||
- bundle exec rails test test/integration/elasticsearch_test.rb
|
- bundle exec rails test test/integration/elasticsearch_test.rb
|
||||||
- bundle exec rspec --tag searchindex --tag ~type:system --profile 10
|
- bundle exec rspec --tag searchindex --tag ~type:system --profile 10
|
||||||
- bundle exec rails test test/integration/report_test.rb
|
|
||||||
|
|
||||||
es:7:
|
es:7:
|
||||||
<<: *template_integration_es
|
<<: *template_integration_es
|
||||||
|
|
|
@ -12,71 +12,71 @@
|
||||||
otrs:6:
|
otrs:6:
|
||||||
<<: *template_integration_otrs
|
<<: *template_integration_otrs
|
||||||
services:
|
services:
|
||||||
- name: registry.znuny.com/docker/zammad-mysql:stable
|
- name: $CI_REGISTRY/docker/zammad-mysql:stable
|
||||||
alias: mysql
|
alias: mysql
|
||||||
- name: registry.znuny.com/docker/zammad-postgresql:stable
|
- name: $CI_REGISTRY/docker/zammad-postgresql:stable
|
||||||
alias: postgresql
|
alias: postgresql
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-db:otrs6
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-db:otrs6
|
||||||
alias: zammad-ci-otrsimport-db
|
alias: zammad-ci-otrsimport-db
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-app:otrs6
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-app:otrs6
|
||||||
alias: zammad-ci-otrsimport-app
|
alias: zammad-ci-otrsimport-app
|
||||||
|
|
||||||
otrs:5:
|
otrs:5:
|
||||||
<<: *template_integration_otrs
|
<<: *template_integration_otrs
|
||||||
services:
|
services:
|
||||||
- name: registry.znuny.com/docker/zammad-mysql:stable
|
- name: $CI_REGISTRY/docker/zammad-mysql:stable
|
||||||
alias: mysql
|
alias: mysql
|
||||||
- name: registry.znuny.com/docker/zammad-postgresql:stable
|
- name: $CI_REGISTRY/docker/zammad-postgresql:stable
|
||||||
alias: postgresql
|
alias: postgresql
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-db:otrs5
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-db:otrs5
|
||||||
alias: zammad-ci-otrsimport-db
|
alias: zammad-ci-otrsimport-db
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-app:otrs5
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-app:otrs5
|
||||||
alias: zammad-ci-otrsimport-app
|
alias: zammad-ci-otrsimport-app
|
||||||
|
|
||||||
otrs:4:
|
otrs:4:
|
||||||
<<: *template_integration_otrs
|
<<: *template_integration_otrs
|
||||||
services:
|
services:
|
||||||
- name: registry.znuny.com/docker/zammad-mysql:stable
|
- name: $CI_REGISTRY/docker/zammad-mysql:stable
|
||||||
alias: mysql
|
alias: mysql
|
||||||
- name: registry.znuny.com/docker/zammad-postgresql:stable
|
- name: $CI_REGISTRY/docker/zammad-postgresql:stable
|
||||||
alias: postgresql
|
alias: postgresql
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-db:otrs4
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-db:otrs4
|
||||||
alias: zammad-ci-otrsimport-db
|
alias: zammad-ci-otrsimport-db
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-app:otrs4
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-app:otrs4
|
||||||
alias: zammad-ci-otrsimport-app
|
alias: zammad-ci-otrsimport-app
|
||||||
|
|
||||||
otrs:33:
|
otrs:33:
|
||||||
<<: *template_integration_otrs
|
<<: *template_integration_otrs
|
||||||
services:
|
services:
|
||||||
- name: registry.znuny.com/docker/zammad-mysql:stable
|
- name: $CI_REGISTRY/docker/zammad-mysql:stable
|
||||||
alias: mysql
|
alias: mysql
|
||||||
- name: registry.znuny.com/docker/zammad-postgresql:stable
|
- name: $CI_REGISTRY/docker/zammad-postgresql:stable
|
||||||
alias: postgresql
|
alias: postgresql
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-db:otrs33
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-db:otrs33
|
||||||
alias: zammad-ci-otrsimport-db
|
alias: zammad-ci-otrsimport-db
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-app:otrs33
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-app:otrs33
|
||||||
alias: zammad-ci-otrsimport-app
|
alias: zammad-ci-otrsimport-app
|
||||||
|
|
||||||
otrs:32:
|
otrs:32:
|
||||||
<<: *template_integration_otrs
|
<<: *template_integration_otrs
|
||||||
services:
|
services:
|
||||||
- name: registry.znuny.com/docker/zammad-mysql:stable
|
- name: $CI_REGISTRY/docker/zammad-mysql:stable
|
||||||
alias: mysql
|
alias: mysql
|
||||||
- name: registry.znuny.com/docker/zammad-postgresql:stable
|
- name: $CI_REGISTRY/docker/zammad-postgresql:stable
|
||||||
alias: postgresql
|
alias: postgresql
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-db:otrs32
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-db:otrs32
|
||||||
alias: zammad-ci-otrsimport-db
|
alias: zammad-ci-otrsimport-db
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-app:otrs32
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-app:otrs32
|
||||||
alias: zammad-ci-otrsimport-app
|
alias: zammad-ci-otrsimport-app
|
||||||
|
|
||||||
otrs:31:
|
otrs:31:
|
||||||
<<: *template_integration_otrs
|
<<: *template_integration_otrs
|
||||||
services:
|
services:
|
||||||
- name: registry.znuny.com/docker/zammad-mysql:stable
|
- name: $CI_REGISTRY/docker/zammad-mysql:stable
|
||||||
alias: mysql
|
alias: mysql
|
||||||
- name: registry.znuny.com/docker/zammad-postgresql:stable
|
- name: $CI_REGISTRY/docker/zammad-postgresql:stable
|
||||||
alias: postgresql
|
alias: postgresql
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-db:otrs31
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-db:otrs31
|
||||||
alias: zammad-ci-otrsimport-db
|
alias: zammad-ci-otrsimport-db
|
||||||
- name: registry.znuny.com/docker/zammad-ci-otrsimport-app:otrs31
|
- name: $CI_REGISTRY/docker/zammad-ci-otrsimport-app:otrs31
|
||||||
alias: zammad-ci-otrsimport-app
|
alias: zammad-ci-otrsimport-app
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
- .rules_singletest
|
- .rules_singletest
|
||||||
before_script:
|
before_script:
|
||||||
- source /etc/profile.d/rvm.sh # ensure RVM is loaded
|
- source /etc/profile.d/rvm.sh # ensure RVM is loaded
|
||||||
- source /opt/rh/rh-nodejs12/enable # ensure Node.js is available
|
- source /opt/rh/rh-nodejs*/enable # ensure Node.js is available
|
||||||
|
|
||||||
rubocop:
|
rubocop:
|
||||||
<<: *template_pre
|
<<: *template_pre
|
||||||
|
@ -39,7 +39,7 @@ brakeman:
|
||||||
artifacts:
|
artifacts:
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
paths:
|
paths:
|
||||||
- tmp/brakeman-report.html
|
- tmp/brakeman-report.html
|
||||||
when: on_failure
|
when: on_failure
|
||||||
script:
|
script:
|
||||||
- bundle install -j $(nproc) --path vendor
|
- bundle install -j $(nproc) --path vendor
|
||||||
|
@ -48,7 +48,7 @@ brakeman:
|
||||||
coffeelint:
|
coffeelint:
|
||||||
<<: *template_pre
|
<<: *template_pre
|
||||||
script:
|
script:
|
||||||
- coffeelint app/
|
- coffeelint --rules ./.coffeelint/rules/* app/
|
||||||
|
|
||||||
bundle-audit:
|
bundle-audit:
|
||||||
<<: *template_pre
|
<<: *template_pre
|
||||||
|
@ -62,8 +62,9 @@ github:
|
||||||
tags:
|
tags:
|
||||||
- deploy
|
- deploy
|
||||||
before_script:
|
before_script:
|
||||||
- "" # no RVM present in deploy ENV
|
- '' # no RVM present in deploy ENV
|
||||||
script:
|
script:
|
||||||
|
- git fetch --unshallow
|
||||||
- script/build/sync_repo.sh git@github.com:zammad/zammad.git
|
- script/build/sync_repo.sh git@github.com:zammad/zammad.git
|
||||||
|
|
||||||
global_refresh_envs:
|
global_refresh_envs:
|
||||||
|
@ -77,7 +78,7 @@ global_refresh_envs:
|
||||||
artifacts:
|
artifacts:
|
||||||
expire_in: 1 day
|
expire_in: 1 day
|
||||||
paths:
|
paths:
|
||||||
- fresh.env
|
- fresh.env
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_MERGE_REQUEST_ID
|
- if: $CI_MERGE_REQUEST_ID
|
||||||
when: never
|
when: never
|
||||||
|
|
14
.pkgr.yml
14
.pkgr.yml
|
@ -56,6 +56,18 @@ targets:
|
||||||
- libimlib2
|
- libimlib2
|
||||||
- libimlib2-dev
|
- libimlib2-dev
|
||||||
- shared-mime-info
|
- shared-mime-info
|
||||||
|
debian-11:
|
||||||
|
dependencies:
|
||||||
|
- curl
|
||||||
|
- elasticsearch|elasticsearch-oss
|
||||||
|
- nginx|apache2
|
||||||
|
- postgresql|mariadb-server
|
||||||
|
- libimlib2
|
||||||
|
- shared-mime-info
|
||||||
|
build_dependencies:
|
||||||
|
- libimlib2
|
||||||
|
- libimlib2-dev
|
||||||
|
- shared-mime-info
|
||||||
ubuntu-16.04:
|
ubuntu-16.04:
|
||||||
dependencies:
|
dependencies:
|
||||||
- curl
|
- curl
|
||||||
|
@ -116,6 +128,6 @@ env:
|
||||||
- ZAMMAD_RAILS_PORT=3000
|
- ZAMMAD_RAILS_PORT=3000
|
||||||
- ZAMMAD_WEBSOCKET_PORT=6042
|
- ZAMMAD_WEBSOCKET_PORT=6042
|
||||||
services:
|
services:
|
||||||
- postgres
|
- postgres:13
|
||||||
before_install: contrib/packager.io/preinstall.sh
|
before_install: contrib/packager.io/preinstall.sh
|
||||||
after_install: contrib/packager.io/postinstall.sh
|
after_install: contrib/packager.io/postinstall.sh
|
||||||
|
|
|
@ -17,7 +17,7 @@ module RuboCop
|
||||||
PATTERN
|
PATTERN
|
||||||
|
|
||||||
def_node_matcher :has_reset?, <<-PATTERN
|
def_node_matcher :has_reset?, <<-PATTERN
|
||||||
$(send _ {:describe :context :it} (_ ...) (hash ... (pair (sym :db_strategy) (sym {:reset :reset_all}))))
|
$(send _ {:describe :context :it :shared_examples} (_ ...) (hash <(pair (sym :db_strategy) (sym {:reset :reset_all})) ...> ))
|
||||||
PATTERN
|
PATTERN
|
||||||
|
|
||||||
MSG = 'Add a `db_strategy: :reset` to your context/decribe when you are creating object manager attributes!'.freeze
|
MSG = 'Add a `db_strategy: :reset` to your context/decribe when you are creating object manager attributes!'.freeze
|
||||||
|
|
17
.rubocop/cop/zammad/prevent_underscore_backport.rb
Normal file
17
.rubocop/cop/zammad/prevent_underscore_backport.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Zammad
|
||||||
|
class PreventUnderscroreBackport < Base
|
||||||
|
MSG = <<~ERROR_MESSAGE.freeze
|
||||||
|
The method __(...) is not available in current stable.
|
||||||
|
ERROR_MESSAGE
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
add_offense(node) if node.method_name.eql? :__
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -13,7 +13,8 @@ module RuboCop
|
||||||
|
|
||||||
def on_new_investigation
|
def on_new_investigation
|
||||||
if processed_source.raw_source.include? '# Copyright (C) 2012-'
|
if processed_source.raw_source.include? '# Copyright (C) 2012-'
|
||||||
update_copyright
|
# Disabled for stable branches.
|
||||||
|
# update_copyright
|
||||||
else
|
else
|
||||||
insert_copyright
|
insert_copyright
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,3 +10,4 @@ require_relative 'cop/zammad/no_to_sym_on_string'
|
||||||
require_relative 'cop/zammad/prefer_negated_if_over_unless'
|
require_relative 'cop/zammad/prefer_negated_if_over_unless'
|
||||||
require_relative 'cop/zammad/update_copyright'
|
require_relative 'cop/zammad/update_copyright'
|
||||||
require_relative 'cop/zammad/forbid_rand'
|
require_relative 'cop/zammad/forbid_rand'
|
||||||
|
require_relative 'cop/zammad/prevent_underscore_backport'
|
||||||
|
|
261
CHANGELOG.md
261
CHANGELOG.md
|
@ -1,15 +1,264 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
## [5.0.0](https://github.com/zammad/zammad/tree/5.0.0) (2021-xx-xx)
|
## [5.0.3](https://github.com/zammad/zammad/tree/5.0.3) (2021-12-07)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/zammad/zammad/compare/5.0.2...5.0.3)
|
||||||
|
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Possibility to specify the order of objects [294](https://github.com/zammad/zammad/issues/294) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
||||||
|
- Display callback urls for third-party applications [3622](https://github.com/zammad/zammad/issues/3622) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||||
|
- Add clear selection action or has changed condition [3821](https://github.com/zammad/zammad/issues/3821) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[specification required](https://github.com/zammad/zammad/labels/specification required)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Missing ticket updates on high load in MariaDB/MySQL environments [3877](https://github.com/zammad/zammad/issues/3877) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Wrong SLA is used (alphabetical order is ignored) [3871](https://github.com/zammad/zammad/issues/3871) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Agent with CREATE only permission acts "on behalf" ticket customer on shared organizations [3872](https://github.com/zammad/zammad/issues/3872) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Provide meaningful modal if report profile tries to use dates out side the filtered date range [3616](https://github.com/zammad/zammad/issues/3616) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[reporting](https://github.com/zammad/zammad/labels/reporting)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Allow `position` to determine an attributes position entirely [3594](https://github.com/zammad/zammad/issues/3594) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)] [[specification required](https://github.com/zammad/zammad/labels/specification required)]
|
||||||
|
- Till label not assigned to corresponding input fields in calendar edit view [3793](https://github.com/zammad/zammad/issues/3793) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- Simple quote characters (`'`) not properly displayed [3846](https://github.com/zammad/zammad/issues/3846) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- Remove api user and password for sipgate integration [3848](https://github.com/zammad/zammad/issues/3848) [[bug](https://github.com/zammad/zammad/labels/bug)] [[admin area](https://github.com/zammad/zammad/labels/admin area)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- No signature on new ticket if email is default message type [3844](https://github.com/zammad/zammad/issues/3844) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Forwarding no longer possible for email and web articles [3855](https://github.com/zammad/zammad/issues/3855) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Default values of new group dialog is different in german vs. english [3851](https://github.com/zammad/zammad/issues/3851) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- If no date is set the UI show it's shown as NaN.NaN.NaN. [3850](https://github.com/zammad/zammad/issues/3850) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Number of to show caller log entries is inconsistent setting wise [3852](https://github.com/zammad/zammad/issues/3852) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[admin area](https://github.com/zammad/zammad/labels/admin area)] [[integration](https://github.com/zammad/zammad/labels/integration)]
|
||||||
|
- Reply all: Duplicate email on changing recipient and cc in a certain way [3825](https://github.com/zammad/zammad/issues/3825) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Removing calendars via UI and API does not check for references [3845](https://github.com/zammad/zammad/issues/3845) [[bug](https://github.com/zammad/zammad/labels/bug)] [[blocker](https://github.com/zammad/zammad/labels/blocker)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[escalation logic](https://github.com/zammad/zammad/labels/escalation logic)]
|
||||||
|
- Email address not shown inside forwarded email [3824](https://github.com/zammad/zammad/issues/3824) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Wrong escalation update time in admin interface is shown [3837](https://github.com/zammad/zammad/issues/3837) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Zammad returns stack error when one tries to remove groups via API [3841](https://github.com/zammad/zammad/issues/3841) [[bug](https://github.com/zammad/zammad/labels/bug)] [[API](https://github.com/zammad/zammad/labels/API)]
|
||||||
|
- Zammad ignores relative GitLab URLs [3830](https://github.com/zammad/zammad/issues/3830) [[bug](https://github.com/zammad/zammad/labels/bug)] [[integration](https://github.com/zammad/zammad/labels/integration)]
|
||||||
|
- If selected value is not part of the restriction of set_fixed_to it should recalculate it with the new value [3822](https://github.com/zammad/zammad/issues/3822) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Invalid group and owner list for tickets created via customer profile [3835](https://github.com/zammad/zammad/issues/3835) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Ticket zoom will loose attachments on rerender [3831](https://github.com/zammad/zammad/issues/3831) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Package installation creates database.yml as `root` and thus breaks the installation until next update [3834](https://github.com/zammad/zammad/issues/3834) [[bug](https://github.com/zammad/zammad/labels/bug)] [[blocker](https://github.com/zammad/zammad/labels/blocker)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Ticket create screen will loose attachments by time [3827](https://github.com/zammad/zammad/issues/3827) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Simple quote characters (`'`) not properly displayed [3733](https://github.com/zammad/zammad/issues/3733) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- When quoting, no breakout from div container possible [3094](https://github.com/zammad/zammad/issues/3094) [[bug](https://github.com/zammad/zammad/labels/bug)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||||
|
- Quoting not working cleanly, if content gets too much [2334](https://github.com/zammad/zammad/issues/2334) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[ticket](https://github.com/zammad/zammad/labels/ticket)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Zammad database credentials are world-readable [3828](https://github.com/zammad/zammad/issues/3828) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Update time SLAs escalates tickets with agent response [3140](https://github.com/zammad/zammad/issues/3140) [[bug](https://github.com/zammad/zammad/labels/bug)] [[escalation logic](https://github.com/zammad/zammad/labels/escalation logic)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
|
||||||
|
## [5.0.2](https://github.com/zammad/zammad/tree/5.0.2) (2021-10-28)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/zammad/zammad/compare/5.0.1...5.0.2)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- When looking for customers, it is no longer possible to change into organizations [3815](https://github.com/zammad/zammad/issues/3815) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- Owner should get cleared if not listed in changed group [3818](https://github.com/zammad/zammad/issues/3818) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Custom date and datetime attributes are filled with dates on creation of tickets/users after update from 4.1 to 5.x [3810](https://github.com/zammad/zammad/issues/3810) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- ActionController::UnknownHttpMethod FRAGE [3807](https://github.com/zammad/zammad/issues/3807) [[bug](https://github.com/zammad/zammad/labels/bug)] [[blocker](https://github.com/zammad/zammad/labels/blocker)] [[overviews](https://github.com/zammad/zammad/labels/overviews)] [[macros](https://github.com/zammad/zammad/labels/macros)]
|
||||||
|
- Remote change of the group id does show it falsly as user change and not render the new value to the ticket [3801](https://github.com/zammad/zammad/issues/3801) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Adding private keys allows adding certificates [3727](https://github.com/zammad/zammad/issues/3727) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Able to create custom fields for existing relation (e. g. ticket.state) - will lead to non bootable Zammad [3811](https://github.com/zammad/zammad/issues/3811) [[bug](https://github.com/zammad/zammad/labels/bug)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Sort order group_by broken (alphabetical) [3800](https://github.com/zammad/zammad/issues/3800) [[bug](https://github.com/zammad/zammad/labels/bug)] [[overviews](https://github.com/zammad/zammad/labels/overviews)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Ticket owner selection is not updated if owner selection should be empty [3809](https://github.com/zammad/zammad/issues/3809) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Chat can't be closed after timeout [2471](https://github.com/zammad/zammad/issues/2471) [[bug](https://github.com/zammad/zammad/labels/bug)] [[chat](https://github.com/zammad/zammad/labels/chat)]
|
||||||
|
- Support workflow mechanism to do pending reminder state hide pending time use case [3790](https://github.com/zammad/zammad/issues/3790) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Cache.clear in postinstall.sh throws ugly errors on fresh installations [3808](https://github.com/zammad/zammad/issues/3808) [[bug](https://github.com/zammad/zammad/labels/bug)] [[blocker](https://github.com/zammad/zammad/labels/blocker)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Example payload in webhook view leads to 500 error [3794](https://github.com/zammad/zammad/issues/3794) [[bug](https://github.com/zammad/zammad/labels/bug)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- OS package upgrade fails (activity_stream_object_id) [3797](https://github.com/zammad/zammad/issues/3797) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
|
||||||
|
## [5.0.1](https://github.com/zammad/zammad/tree/5.0.1) (2021-10-08)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/zammad/zammad/compare/5.0.0...5.0.1)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Bug Report 4.1.x Overview Sort - Grouped by user [3737](https://github.com/zammad/zammad/issues/3737) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[overviews](https://github.com/zammad/zammad/labels/overviews)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Article box opening on tickets with no changes [3789](https://github.com/zammad/zammad/issues/3789) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- UploadCacheCleanupJob does not execute [3787](https://github.com/zammad/zammad/issues/3787) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- lib/fill_db.rb fails to work in production environments [3788](https://github.com/zammad/zammad/issues/3788) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
|
||||||
|
## [5.0.0](https://github.com/zammad/zammad/tree/5.0.0) (2021-10-05)
|
||||||
|
|
||||||
[Full Changelog](https://github.com/zammad/zammad/compare/4.1.0...5.0.0)
|
[Full Changelog](https://github.com/zammad/zammad/compare/4.1.0...5.0.0)
|
||||||
|
|
||||||
**Implemented enhancements:**
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Core Workflow: Add organization condition attributes for object User [3779](https://github.com/zammad/zammad/issues/3779) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- No script content (e. g. javascript) in emails [3365](https://github.com/zammad/zammad/issues/3365) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||||
|
- Read-only custom objects [2102](https://github.com/zammad/zammad/issues/2102) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Granular admin permission for google channel is missing [3194](https://github.com/zammad/zammad/issues/3194) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||||
|
- Use country codes (e.g. `DE` or `ES`) for knowledgebase answer selection [3574](https://github.com/zammad/zammad/issues/3574) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- New email account expert view cannot be opened without filling in all fields [3137](https://github.com/zammad/zammad/issues/3137) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- Don't provide option to create API-Token if authentication via API token is disabled [3168](https://github.com/zammad/zammad/issues/3168) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- Define default "stay on tab" / "close tab" behavior [257](https://github.com/zammad/zammad/issues/257) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Space after ticket hook in ticket/zoom [3265](https://github.com/zammad/zammad/issues/3265) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||||
|
- Enhance LDAP login to not affect "failed logins" [2389](https://github.com/zammad/zammad/issues/2389) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[LDAP](https://github.com/zammad/zammad/labels/LDAP)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Detect Jira follow-ups [3695](https://github.com/zammad/zammad/issues/3695) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||||
|
- csv import example file download (organizations) is empty [3514](https://github.com/zammad/zammad/issues/3514) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||||
|
- Add bulk option to extended search [445](https://github.com/zammad/zammad/issues/445) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- MessageBird integration [3672](https://github.com/zammad/zammad/issues/3672) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[channel](https://github.com/zammad/zammad/labels/channel)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Add attachment via Trigger and scheduler if wanted by admin [2485](https://github.com/zammad/zammad/issues/2485) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[admin area](https://github.com/zammad/zammad/labels/admin area)] [[trigger](https://github.com/zammad/zammad/labels/trigger)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Tag KB Answers [2612](https://github.com/zammad/zammad/issues/2612) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- Backtrace lines pollute log [3556](https://github.com/zammad/zammad/issues/3556) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[authentication](https://github.com/zammad/zammad/labels/authentication)]
|
||||||
|
- Visualise locked users in UI and make them unlock-able for admin [2565](https://github.com/zammad/zammad/issues/2565) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Representation of inactive customers and orgnizations [3302](https://github.com/zammad/zammad/issues/3302) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- No possibility to enforce auto response if one of the blocking auto response mail header exists [3667](https://github.com/zammad/zammad/issues/3667) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||||
|
- REST doc of Online Notification controler is outdated/wrong and expand param is missing. [3635](https://github.com/zammad/zammad/issues/3635) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||||
|
- Scroll background instead of foreground [978](https://github.com/zammad/zammad/issues/978) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- Log if a active user (in UI) has been logged out due to SessionTimeout [3614](https://github.com/zammad/zammad/issues/3614) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||||
|
- The rake task `zammad:package:migrate` does not execute migrations for linked packages. [3606](https://github.com/zammad/zammad/issues/3606) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[developer experience](https://github.com/zammad/zammad/labels/developer experience)]
|
||||||
|
- Accept 2xx as response for requests in UserAgent [3573](https://github.com/zammad/zammad/pull/3573) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||||
|
- Don't fail job is `deletable_id` is no longer available [3536](https://github.com/zammad/zammad/issues/3536) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[data privacy](https://github.com/zammad/zammad/labels/data privacy)]
|
||||||
|
- Add console output for searchindex rebuild status [3562](https://github.com/zammad/zammad/issues/3562) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
|
||||||
|
- Display minutes for session timeouts instead of seconds [3575](https://github.com/zammad/zammad/issues/3575) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[admin area](https://github.com/zammad/zammad/labels/admin area)]
|
||||||
|
|
||||||
**Fixed bugs:**
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Inconstant alignment in the listing of attachments/submit button in new article area [3773](https://github.com/zammad/zammad/issues/3773) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Improve contrasts in answer search for articles [3783](https://github.com/zammad/zammad/issues/3783) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- escaped 'Set fixed' workflows don't refresh set values on active ticket sessions [3757](https://github.com/zammad/zammad/issues/3757) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- ObjectManager Attribute without screen attribute causes CoreWorkflows migration to fail [3781](https://github.com/zammad/zammad/issues/3781) [[bug](https://github.com/zammad/zammad/labels/bug)] [[migration / update](https://github.com/zammad/zammad/labels/migration / update)]
|
||||||
|
- Zammad preflight check warning output causes Syntax-Error in postinstall.sh and failing installation [2674](https://github.com/zammad/zammad/issues/2674) [[bug](https://github.com/zammad/zammad/labels/bug)] [[migration / update](https://github.com/zammad/zammad/labels/migration / update)]
|
||||||
|
- Shared organization issue (Create your first ticket) [2780](https://github.com/zammad/zammad/issues/2780) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||||
|
- Force users to reload after system migration [3776](https://github.com/zammad/zammad/issues/3776) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Unable to cancel attachment upload [2351](https://github.com/zammad/zammad/issues/2351) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- [BUG] miss spell in db/migrate/20210923172256_issue_2619_kb_header_link_color.rb [3777](https://github.com/zammad/zammad/issues/3777) [[bug](https://github.com/zammad/zammad/labels/bug)] [[blocker](https://github.com/zammad/zammad/labels/blocker)] [[migration / update](https://github.com/zammad/zammad/labels/migration / update)]
|
||||||
|
- KB header and footer link-color not changeable [2619](https://github.com/zammad/zammad/issues/2619) [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)] [[specification required](https://github.com/zammad/zammad/labels/specification required)] [[theming / skinning](https://github.com/zammad/zammad/labels/theming / skinning)]
|
||||||
|
- Syntax errors break scheduler job for good [3028](https://github.com/zammad/zammad/issues/3028) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Existing tickets: New article modal with padding-left: 0; padding-right: 0; [3772](https://github.com/zammad/zammad/issues/3772) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Possible race condition causing OTRS import to fail [3765](https://github.com/zammad/zammad/issues/3765) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Incorrect alignment in the listing of attachments when creating a ticket [3746](https://github.com/zammad/zammad/issues/3746) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Saved conditions break on selections without reloading [3758](https://github.com/zammad/zammad/issues/3758) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Misleading view of user icons which are on vacation and disabled [3075](https://github.com/zammad/zammad/issues/3075) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||||
|
- User with user_id 1 is show in admin interface (which should not) [3755](https://github.com/zammad/zammad/issues/3755) [[bug](https://github.com/zammad/zammad/labels/bug)] [[blocker](https://github.com/zammad/zammad/labels/blocker)]
|
||||||
|
- Unable to close tickets in certran cases if core workflow is used [3710](https://github.com/zammad/zammad/issues/3710) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Login failed after upgrade to zammad 5.0 [3759](https://github.com/zammad/zammad/issues/3759) [[bug](https://github.com/zammad/zammad/labels/bug)] [[blocker](https://github.com/zammad/zammad/labels/blocker)] [[migration / update](https://github.com/zammad/zammad/labels/migration / update)]
|
||||||
|
- Unable to create a ticket in web app if default for additional boolean value is FALSE [3762](https://github.com/zammad/zammad/issues/3762) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- User search / selection does not always work if you want to correct your search [3696](https://github.com/zammad/zammad/issues/3696) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Unable to create new organization [3751](https://github.com/zammad/zammad/issues/3751) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Core Workflow "is not" operator is working unexpected [3752](https://github.com/zammad/zammad/issues/3752) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- scrollPageHeader disappears when answering via email [3736](https://github.com/zammad/zammad/issues/3736) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- Scheduler ignores "disable notifications == no" [3684](https://github.com/zammad/zammad/issues/3684) [[bug](https://github.com/zammad/zammad/labels/bug)] [[notification](https://github.com/zammad/zammad/labels/notification)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Notes on existing ticks are discarded by editing profile settings [3088](https://github.com/zammad/zammad/issues/3088) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- Owner selection empty or pending reminder data picker not shown if customer field has content but no customer is selected (in ticket create screen) [3743](https://github.com/zammad/zammad/issues/3743) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Removal of Microsoft365-Channel not possible via UI when having no email address relation [3741](https://github.com/zammad/zammad/issues/3741) [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)]
|
||||||
|
- Removal of Google-Channel not possible via UI when having a normal email channel of same account [3203](https://github.com/zammad/zammad/issues/3203) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[channel](https://github.com/zammad/zammad/labels/channel)]
|
||||||
|
- Core-Workflow: Option manipulation of boolean attributes not possible [3740](https://github.com/zammad/zammad/issues/3740) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Core Workflow: Show hidden attributes on group selection (ticket edit) [3739](https://github.com/zammad/zammad/issues/3739) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- OTRS migration doesn't import article creation time [3235](https://github.com/zammad/zammad/issues/3235) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Spaces stay URL-encoded when going back to search results on KB for agents [3378](https://github.com/zammad/zammad/issues/3378) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- Not working unpack of mail body if decrypted body is in signed-data attachment [3705](https://github.com/zammad/zammad/issues/3705) [[bug](https://github.com/zammad/zammad/labels/bug)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||||
|
- It should be possible to show attributes which are configured `shown` false [3726](https://github.com/zammad/zammad/issues/3726) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- admin.core_workflow permission is missing [3732](https://github.com/zammad/zammad/issues/3732) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- AttributeManager doesn't display boolean default values within the UI [3271](https://github.com/zammad/zammad/issues/3271) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
||||||
|
- Spaces in front of mentions can cause partly content removal [3717](https://github.com/zammad/zammad/issues/3717) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Drop files here not disappearing after dropping a file [3725](https://github.com/zammad/zammad/issues/3725) [[bug](https://github.com/zammad/zammad/labels/bug)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- ⚙ -> Manage -> Calendars -> Edit: "Subscribe to public holidays in" sorted wrong [2528](https://github.com/zammad/zammad/issues/2528) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- Pending reminder notifications are not sent if ticket is opened in tab [2931](https://github.com/zammad/zammad/issues/2931) [[bug](https://github.com/zammad/zammad/labels/bug)] [[ticket](https://github.com/zammad/zammad/labels/ticket)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- Fields are falsey displayed as mandatory if they contain historic screen values [3721](https://github.com/zammad/zammad/issues/3721) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- `ticket.customer_id` is `current_user` breaks email fetching [3503](https://github.com/zammad/zammad/issues/3503) [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||||
|
- Scheduler jobs don't ensure current ticket information if they're running a long time [3329](https://github.com/zammad/zammad/issues/3329) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- groups selection for customer panel is not working anymore (with core workflows) [3713](https://github.com/zammad/zammad/issues/3713) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Scheduler Time offset [457](https://github.com/zammad/zammad/issues/457) [[bug](https://github.com/zammad/zammad/labels/bug)] [[trigger](https://github.com/zammad/zammad/labels/trigger)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- "Warten auf Erinnerung" - Inconsistent timestamps [2366](https://github.com/zammad/zammad/issues/2366) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Ical escaltion update the calendar entry to 2:00 am [2157](https://github.com/zammad/zammad/issues/2157) [[bug](https://github.com/zammad/zammad/labels/bug)] [[notification](https://github.com/zammad/zammad/labels/notification)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Inheritance of out of office settings [3694](https://github.com/zammad/zammad/issues/3694) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Core worflow perform action "select" should not make multiple values selectable [3712](https://github.com/zammad/zammad/issues/3712) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Setting for CheckMK not saved in zammad [3707](https://github.com/zammad/zammad/issues/3707) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Any user can be set as a replacement for out of office [2293](https://github.com/zammad/zammad/issues/2293) [[bug](https://github.com/zammad/zammad/labels/bug)] [[personal settings/menu](https://github.com/zammad/zammad/labels/personal settings/menu)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Core Workflow Implementation [3709](https://github.com/zammad/zammad/issues/3709) [[feature backlog](https://github.com/zammad/zammad/labels/feature backlog)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[core](https://github.com/zammad/zammad/labels/core)] [[core workflows](https://github.com/zammad/zammad/labels/core workflows)]
|
||||||
|
- Can't remove auto assignment timeout [2544](https://github.com/zammad/zammad/issues/2544) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Assign Follow ups has no affect [2455](https://github.com/zammad/zammad/issues/2455) [[bug](https://github.com/zammad/zammad/labels/bug)] [[ticket](https://github.com/zammad/zammad/labels/ticket)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- The calendar shows Chilean holidays one day in advance [3706](https://github.com/zammad/zammad/issues/3706) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Organization removal does not clear organization_id from Tickets [3703](https://github.com/zammad/zammad/issues/3703) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Merge not possible with not set, required attributes [2634](https://github.com/zammad/zammad/issues/2634) [[bug](https://github.com/zammad/zammad/labels/bug)] [[ticket](https://github.com/zammad/zammad/labels/ticket)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
||||||
|
- Merging tickets doesn’t trigger notification for target ticket [3105](https://github.com/zammad/zammad/issues/3105) [[bug](https://github.com/zammad/zammad/labels/bug)] [[notification](https://github.com/zammad/zammad/labels/notification)] [[ticket](https://github.com/zammad/zammad/labels/ticket)]
|
||||||
|
- One of DB migrations timestamp breaks migrations queue [3702](https://github.com/zammad/zammad/issues/3702) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Tags in ticket create screen displayed wrong [3701](https://github.com/zammad/zammad/issues/3701) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[regression](https://github.com/zammad/zammad/labels/regression)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- Custom date attributes provide inconsistent sorting within grouping [3663](https://github.com/zammad/zammad/issues/3663) [[bug](https://github.com/zammad/zammad/labels/bug)] [[overviews](https://github.com/zammad/zammad/labels/overviews)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Mix of binary encoded ISO-8859-1 data in header fields (e.g. to) fails mail processing [3697](https://github.com/zammad/zammad/issues/3697) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||||
|
- Zammad sends mails even though there is no email address visible in TO or CC fields (any more) [3554](https://github.com/zammad/zammad/issues/3554) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- Error 500 / zammad {"error":" 123456 is out of range for ActiveModel::Type::Integer with limit 4 bytes"} [3647](https://github.com/zammad/zammad/issues/3647) [[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)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
|
||||||
|
- When replying, quote article content only [3539](https://github.com/zammad/zammad/issues/3539) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Trigger ignores changes on some Ticket attributes [3428](https://github.com/zammad/zammad/issues/3428) [[bug](https://github.com/zammad/zammad/labels/bug)] [[trigger](https://github.com/zammad/zammad/labels/trigger)]
|
||||||
|
- KB search does not allow pagination on endpoint [3068](https://github.com/zammad/zammad/issues/3068) [[bug](https://github.com/zammad/zammad/labels/bug)] [[API](https://github.com/zammad/zammad/labels/API)] [[search](https://github.com/zammad/zammad/labels/search)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- “You have not created a ticket yet” shown but I already have closed tickets [3053](https://github.com/zammad/zammad/issues/3053) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[overviews](https://github.com/zammad/zammad/labels/overviews)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[🚧 refactoring 2.0 🚧](https://github.com/zammad/zammad/labels/🚧 refactoring 2.0 🚧)]
|
||||||
|
- Support HTML5 'required' attribute in forms [1890](https://github.com/zammad/zammad/pull/1890)
|
||||||
|
- Freshdesk import works not with more then 30_000 tickets [3681](https://github.com/zammad/zammad/issues/3681) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Pagination for knowledge base answer sorting breaks sorting [3660](https://github.com/zammad/zammad/issues/3660) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[admin area](https://github.com/zammad/zammad/labels/admin area)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- Zendesk-Import `Connection reset by peer` cancels import [3583](https://github.com/zammad/zammad/issues/3583) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)] [[waiting for feedback](https://github.com/zammad/zammad/labels/waiting for feedback)]
|
||||||
|
- Prefer HTTP standard header "From" over custom "X-On-Behalf-Of" for impersonation [3113](https://github.com/zammad/zammad/issues/3113) [[API](https://github.com/zammad/zammad/labels/API)] [[help appreciated](https://github.com/zammad/zammad/labels/help appreciated)] [[authentication](https://github.com/zammad/zammad/labels/authentication)] [[hacktoberfest](https://github.com/zammad/zammad/labels/hacktoberfest)] [[:construction: refactoring :construction:](https://github.com/zammad/zammad/labels/:construction: refactoring :construction:)] [[specification required](https://github.com/zammad/zammad/labels/specification required)] [[good first issue](https://github.com/zammad/zammad/labels/good first issue)] [[deprecation](https://github.com/zammad/zammad/labels/deprecation)]
|
||||||
|
- Do not send `domain` and migrate `domain` out of Google and Microsoft 365 channels [3669](https://github.com/zammad/zammad/issues/3669) [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- high organization user count relates to bad performance [3648](https://github.com/zammad/zammad/issues/3648) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[performance](https://github.com/zammad/zammad/labels/performance)]
|
||||||
|
- Removing organizations removes user and ticket as well [3688](https://github.com/zammad/zammad/issues/3688) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Wrong user is used when "X-On-Behalf-Of” header value is an email that starts with digits [2851](https://github.com/zammad/zammad/issues/2851) [[bug](https://github.com/zammad/zammad/labels/bug)] [[API](https://github.com/zammad/zammad/labels/API)]
|
||||||
|
- Allow out of office for one day without setting two days [3590](https://github.com/zammad/zammad/issues/3590) [[bug](https://github.com/zammad/zammad/labels/bug)] [[personal settings/menu](https://github.com/zammad/zammad/labels/personal settings/menu)]
|
||||||
|
- FreshDesk Import doesn't pull in auto-assign domain(s) for organizations [3687](https://github.com/zammad/zammad/issues/3687) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- FreshDesk Import brings in all users as inactive [3689](https://github.com/zammad/zammad/issues/3689) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- KB Public UI icons are misspaced [3680](https://github.com/zammad/zammad/issues/3680) [[bug](https://github.com/zammad/zammad/labels/bug)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- FreshDesk Import Error - undefined method `body' for 10:Integer [3661](https://github.com/zammad/zammad/issues/3661) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Cannot select multiple tickets in ticket overview with shift+click in Firefox [3449](https://github.com/zammad/zammad/issues/3449) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[overviews](https://github.com/zammad/zammad/labels/overviews)]
|
||||||
|
- "Drop files here" drag area not always hiding [3460](https://github.com/zammad/zammad/issues/3460) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[help appreciated](https://github.com/zammad/zammad/labels/help appreciated)]
|
||||||
|
- CTI Callerlog signaling hides "+" for new ticket if more than one user is found [2930](https://github.com/zammad/zammad/issues/2930) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- Zendesk URL check fails with Net::HTTPForbidden 403 error [3679](https://github.com/zammad/zammad/issues/3679) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Mac Mail inline PDF destroys E-Mail-Body [2407](https://github.com/zammad/zammad/issues/2407) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
|
||||||
|
- Outdated urls on security page (third-party logins) [3627](https://github.com/zammad/zammad/issues/3627) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Duplicated article after bulk action was executed for newly created tickets without a browser refresh in between [3568](https://github.com/zammad/zammad/issues/3568) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Text Editor deletes text unrecoverably if quicktext was canceled with Ctrl+Backspace [2886](https://github.com/zammad/zammad/issues/2886) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[ticket](https://github.com/zammad/zammad/labels/ticket)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- The defined ignored attributes in the popover context are not working [3668](https://github.com/zammad/zammad/issues/3668) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- Ensure Upload Cache files are removed after grace period [3579](https://github.com/zammad/zammad/issues/3579) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Package installation fails because of too long file names [3666](https://github.com/zammad/zammad/issues/3666) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Package migrations fail on package installation or update due to codebase self-modification [3580](https://github.com/zammad/zammad/issues/3580) [[bug](https://github.com/zammad/zammad/labels/bug)] [[core](https://github.com/zammad/zammad/labels/core)]
|
||||||
|
- saving Boolean field with empty values [2973](https://github.com/zammad/zammad/issues/2973) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- CTI-API should handle doubled requests better [3247](https://github.com/zammad/zammad/issues/3247) [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[CTI](https://github.com/zammad/zammad/labels/CTI)] [[specification required](https://github.com/zammad/zammad/labels/specification required)]
|
||||||
|
- In some situations the chat button is visible without a active websocket connection [2258](https://github.com/zammad/zammad/issues/2258) [[bug](https://github.com/zammad/zammad/labels/bug)] [[chat](https://github.com/zammad/zammad/labels/chat)]
|
||||||
|
- Fixes mistypes in article_view.coffee [3658](https://github.com/zammad/zammad/pull/3658)
|
||||||
|
- Creating and editing users via office 365 failes with Image source is invalid [3617](https://github.com/zammad/zammad/issues/3617) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- FreshDesk Import fails with timeout error [3653](https://github.com/zammad/zammad/issues/3653) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Chat widget cannot load CSS on (sub)domains that contain "ws" characters [3654](https://github.com/zammad/zammad/issues/3654) [[bug](https://github.com/zammad/zammad/labels/bug)] [[chat](https://github.com/zammad/zammad/labels/chat)]
|
||||||
|
- timezone issue with elasticsearch [2085](https://github.com/zammad/zammad/issues/2085) [[duplicate](https://github.com/zammad/zammad/labels/duplicate)]
|
||||||
|
- Invalid html signature detection for exchange warning boxes [3571](https://github.com/zammad/zammad/issues/3571) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Searching for integer fields does not work as expected [3599](https://github.com/zammad/zammad/issues/3599) [[bug](https://github.com/zammad/zammad/labels/bug)] [[search](https://github.com/zammad/zammad/labels/search)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- FreshDesk import fails on certain custom field types [3628](https://github.com/zammad/zammad/issues/3628) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Freshdesk import subdomain selection fails [3631](https://github.com/zammad/zammad/issues/3631) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Search results missing for users with two names within lastname [3598](https://github.com/zammad/zammad/issues/3598) [[bug](https://github.com/zammad/zammad/labels/bug)] [[search](https://github.com/zammad/zammad/labels/search)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- New location notification with X-On-Behalf [3611](https://github.com/zammad/zammad/issues/3611) [[bug](https://github.com/zammad/zammad/labels/bug)] [[API](https://github.com/zammad/zammad/labels/API)] [[notification](https://github.com/zammad/zammad/labels/notification)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- State list of tickets ignore locale for their sorting [3335](https://github.com/zammad/zammad/issues/3335) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[ticket](https://github.com/zammad/zammad/labels/ticket)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Newly created users are only shown in the admin interface after reload [3050](https://github.com/zammad/zammad/issues/3050) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[frontend / JS app](https://github.com/zammad/zammad/labels/frontend / JS app)]
|
||||||
|
- Migration 3.6.1 to 4.1 : No configured business hours found! [3641](https://github.com/zammad/zammad/issues/3641) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Verification tokens questionable behavior in developer mode [3116](https://github.com/zammad/zammad/issues/3116) [[developer experience](https://github.com/zammad/zammad/labels/developer experience)]
|
||||||
|
- Replacing a mention with a new leads to mentioning both agents [3636](https://github.com/zammad/zammad/issues/3636) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Attachment order is wrong [3584](https://github.com/zammad/zammad/issues/3584) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
|
||||||
|
- When disconnecting a Chat, customer can still continue posting messages [2559](https://github.com/zammad/zammad/issues/2559) [[bug](https://github.com/zammad/zammad/labels/bug)] [[chat](https://github.com/zammad/zammad/labels/chat)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- some macros are hidden in the drag & drop- overview with a small screen size [3359](https://github.com/zammad/zammad/issues/3359) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Add redis as an (optional) session store backend [3450](https://github.com/zammad/zammad/pull/3450)
|
||||||
|
- Text Module widget is broken/hidden after pressing backspace (once) [3637](https://github.com/zammad/zammad/issues/3637) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Mention widget show same users several times (if rest requests will return in different order) [3639](https://github.com/zammad/zammad/issues/3639) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- The rebuild of the index is failing [3633](https://github.com/zammad/zammad/issues/3633) [[bug](https://github.com/zammad/zammad/labels/bug)] [[chat](https://github.com/zammad/zammad/labels/chat)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Monitoring endpoint complains about `Failed to run SessionTimeoutJob.perform_now after 10 tries #<NoMethodError: undefined method `id' for nil:NilClass` [3632](https://github.com/zammad/zammad/issues/3632) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Updating package-based installations can break the front end. [3629](https://github.com/zammad/zammad/issues/3629) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- backup: stop using su for pg_dump [3352](https://github.com/zammad/zammad/pull/3352) [[waiting for feedback](https://github.com/zammad/zammad/labels/waiting for feedback)]
|
||||||
|
- Reply-to header ignored if from contains a system address [2551](https://github.com/zammad/zammad/issues/2551) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Maintenance: Fix handling of test that require secrets not to run for GitHub PRs. [3626](https://github.com/zammad/zammad/pull/3626)
|
||||||
|
- replacing find -mtime with find -mmin [3570](https://github.com/zammad/zammad/pull/3570)
|
||||||
|
- Ensure to migrate old calendar URLs for google calendar sources [3618](https://github.com/zammad/zammad/issues/3618) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[regression](https://github.com/zammad/zammad/labels/regression)]
|
||||||
|
- Mentions/Subscribe with read permissions [3615](https://github.com/zammad/zammad/issues/3615) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Chat-Message references not cleared with data privacy task [3610](https://github.com/zammad/zammad/issues/3610) [[bug](https://github.com/zammad/zammad/labels/bug)] [[data privacy](https://github.com/zammad/zammad/labels/data privacy)]
|
||||||
|
- Overview names are hidden in mobile view for RTL languages [3500](https://github.com/zammad/zammad/issues/3500) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[mobile](https://github.com/zammad/zammad/labels/mobile)]
|
||||||
|
- SMTP-Attachments missing when using the API under high load [2991](https://github.com/zammad/zammad/issues/2991) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Inline image conversion from jpeg to png causes huge overhead [3538](https://github.com/zammad/zammad/issues/3538) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- session timeout does regulary logout active users before timeout because of dead old sessions [3605](https://github.com/zammad/zammad/issues/3605) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Zendesk import creates new ticket numbers rather than using the Zendesk ticket IDs also as ticket numbers in Zammad [3593](https://github.com/zammad/zammad/issues/3593) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)] [[ticket](https://github.com/zammad/zammad/labels/ticket)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- wrong behaviour for the disabled option in session timeout [3600](https://github.com/zammad/zammad/issues/3600) [[bug](https://github.com/zammad/zammad/labels/bug)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Header links are not responsive [3133](https://github.com/zammad/zammad/issues/3133) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- Update description of the session timeout setting [3597](https://github.com/zammad/zammad/issues/3597) [[bug](https://github.com/zammad/zammad/labels/bug)] [[admin area](https://github.com/zammad/zammad/labels/admin area)]
|
||||||
|
- Escalation calculation breaks data privacy ticket deletion [3588](https://github.com/zammad/zammad/issues/3588) [[bug](https://github.com/zammad/zammad/labels/bug)] [[data privacy](https://github.com/zammad/zammad/labels/data privacy)]
|
||||||
|
- `SessionTimeoutJob.perform_now` fails if user no longer exists [3586](https://github.com/zammad/zammad/issues/3586) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Robots.txt should allow indexing Knowledge Base on custom URLs [3164](https://github.com/zammad/zammad/issues/3164) [[bug](https://github.com/zammad/zammad/labels/bug)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- DataPrivacy may fail to clear user relations [3585](https://github.com/zammad/zammad/issues/3585) [[bug](https://github.com/zammad/zammad/labels/bug)] [[data privacy](https://github.com/zammad/zammad/labels/data privacy)]
|
||||||
|
- SessionTimeoutJob.perform_now scheduler job fails on update if scheduler process wasn't restarted [3581](https://github.com/zammad/zammad/issues/3581) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- Freshdesk import attribute ID mapping lookup constants fail application boot in systems with changed default names [3582](https://github.com/zammad/zammad/issues/3582) [[bug](https://github.com/zammad/zammad/labels/bug)]
|
||||||
|
- mentioned KB entries with the same title in different categories can not distinguished [3559](https://github.com/zammad/zammad/issues/3559) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)] [[knowledge base](https://github.com/zammad/zammad/labels/knowledge base)]
|
||||||
|
- Invalid auto assignment conditions may break ticket view for unassigned tickets [3567](https://github.com/zammad/zammad/issues/3567) [[bug](https://github.com/zammad/zammad/labels/bug)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[prioritised by payment](https://github.com/zammad/zammad/labels/prioritised by payment)]
|
||||||
|
- Add Greek translation for chat [3569](https://github.com/zammad/zammad/pull/3569) [[chat](https://github.com/zammad/zammad/labels/chat)] [[translation](https://github.com/zammad/zammad/labels/translation)]
|
||||||
|
- Freshdesk [865](https://github.com/zammad/zammad/issues/865) [[feature backlog](https://github.com/zammad/zammad/labels/feature backlog)] [[import](https://github.com/zammad/zammad/labels/import)]
|
||||||
|
- Old JQuery version delivered [3431](https://github.com/zammad/zammad/issues/3431) [[bug](https://github.com/zammad/zammad/labels/bug)] [[chat](https://github.com/zammad/zammad/labels/chat)]
|
||||||
|
|
8
Gemfile
8
Gemfile
|
@ -1,9 +1,9 @@
|
||||||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://gems.sutty.nl'
|
||||||
|
|
||||||
# core - base
|
# core - base
|
||||||
ruby '2.7.4'
|
ruby '2.7.5'
|
||||||
gem 'rails', '~> 6.0'
|
gem 'rails', '~> 6.0'
|
||||||
|
|
||||||
# core - rails additions
|
# core - rails additions
|
||||||
|
@ -227,10 +227,6 @@ group :development, :test do
|
||||||
|
|
||||||
# Slack helper for testing
|
# Slack helper for testing
|
||||||
gem 'slack-ruby-client', require: false
|
gem 'slack-ruby-client', require: false
|
||||||
|
|
||||||
# Can be used to detect for example the current
|
|
||||||
# operating system in tests, to handle things differently.
|
|
||||||
gem 'os'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Want to extend Zammad with additional gems?
|
# Want to extend Zammad with additional gems?
|
||||||
|
|
181
Gemfile.lock
181
Gemfile.lock
|
@ -17,54 +17,54 @@ GIT
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://gems.sutty.nl/
|
||||||
specs:
|
specs:
|
||||||
aasm (5.2.0)
|
aasm (5.2.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
actioncable (6.0.4.1)
|
actioncable (6.0.4.7)
|
||||||
actionpack (= 6.0.4.1)
|
actionpack (= 6.0.4.7)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (6.0.4.1)
|
actionmailbox (6.0.4.7)
|
||||||
actionpack (= 6.0.4.1)
|
actionpack (= 6.0.4.7)
|
||||||
activejob (= 6.0.4.1)
|
activejob (= 6.0.4.7)
|
||||||
activerecord (= 6.0.4.1)
|
activerecord (= 6.0.4.7)
|
||||||
activestorage (= 6.0.4.1)
|
activestorage (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
actionmailer (6.0.4.1)
|
actionmailer (6.0.4.7)
|
||||||
actionpack (= 6.0.4.1)
|
actionpack (= 6.0.4.7)
|
||||||
actionview (= 6.0.4.1)
|
actionview (= 6.0.4.7)
|
||||||
activejob (= 6.0.4.1)
|
activejob (= 6.0.4.7)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (6.0.4.1)
|
actionpack (6.0.4.7)
|
||||||
actionview (= 6.0.4.1)
|
actionview (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
rack (~> 2.0, >= 2.0.8)
|
rack (~> 2.0, >= 2.0.8)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actiontext (6.0.4.1)
|
actiontext (6.0.4.7)
|
||||||
actionpack (= 6.0.4.1)
|
actionpack (= 6.0.4.7)
|
||||||
activerecord (= 6.0.4.1)
|
activerecord (= 6.0.4.7)
|
||||||
activestorage (= 6.0.4.1)
|
activestorage (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (6.0.4.1)
|
actionview (6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||||
activejob (6.0.4.1)
|
activejob (6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (6.0.4.1)
|
activemodel (6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
activerecord (6.0.4.1)
|
activerecord (6.0.4.7)
|
||||||
activemodel (= 6.0.4.1)
|
activemodel (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
activerecord-import (1.2.0)
|
activerecord-import (1.2.0)
|
||||||
activerecord (>= 3.2)
|
activerecord (>= 3.2)
|
||||||
activerecord-nulldb-adapter (0.7.0)
|
activerecord-nulldb-adapter (0.7.0)
|
||||||
|
@ -75,12 +75,12 @@ GEM
|
||||||
multi_json (~> 1.11, >= 1.11.2)
|
multi_json (~> 1.11, >= 1.11.2)
|
||||||
rack (>= 2.0.8, < 3)
|
rack (>= 2.0.8, < 3)
|
||||||
railties (>= 5.2.4.1)
|
railties (>= 5.2.4.1)
|
||||||
activestorage (6.0.4.1)
|
activestorage (6.0.4.7)
|
||||||
actionpack (= 6.0.4.1)
|
actionpack (= 6.0.4.7)
|
||||||
activejob (= 6.0.4.1)
|
activejob (= 6.0.4.7)
|
||||||
activerecord (= 6.0.4.1)
|
activerecord (= 6.0.4.7)
|
||||||
marcel (~> 1.0.0)
|
marcel (~> 1.0.0)
|
||||||
activesupport (6.0.4.1)
|
activesupport (6.0.4.7)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
|
@ -93,6 +93,9 @@ GEM
|
||||||
argon2 (2.0.3)
|
argon2 (2.0.3)
|
||||||
ffi (~> 1.14)
|
ffi (~> 1.14)
|
||||||
ffi-compiler (~> 1.0)
|
ffi-compiler (~> 1.0)
|
||||||
|
argon2 (2.0.3-x86_64-linux-musl)
|
||||||
|
ffi (~> 1.14)
|
||||||
|
ffi-compiler (~> 1.0)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
async (1.29.1)
|
async (1.29.1)
|
||||||
console (~> 1.10)
|
console (~> 1.10)
|
||||||
|
@ -121,11 +124,14 @@ GEM
|
||||||
tzinfo
|
tzinfo
|
||||||
bootsnap (1.9.1)
|
bootsnap (1.9.1)
|
||||||
msgpack (~> 1.0)
|
msgpack (~> 1.0)
|
||||||
|
bootsnap (1.9.1-x86_64-linux-musl)
|
||||||
|
msgpack (~> 1.0)
|
||||||
brakeman (5.1.1)
|
brakeman (5.1.1)
|
||||||
browser (5.3.1)
|
browser (5.3.1)
|
||||||
buftok (0.2.0)
|
buftok (0.2.0)
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
byebug (11.1.3)
|
byebug (11.1.3)
|
||||||
|
byebug (11.1.3-x86_64-linux-musl)
|
||||||
capybara (3.35.3)
|
capybara (3.35.3)
|
||||||
addressable
|
addressable
|
||||||
mini_mime (>= 0.1.3)
|
mini_mime (>= 0.1.3)
|
||||||
|
@ -169,6 +175,7 @@ GEM
|
||||||
daemons (1.4.1)
|
daemons (1.4.1)
|
||||||
dalli (2.7.11)
|
dalli (2.7.11)
|
||||||
debug_inspector (1.1.0)
|
debug_inspector (1.1.0)
|
||||||
|
debug_inspector (1.1.0-x86_64-linux-musl)
|
||||||
delayed_job (4.1.9)
|
delayed_job (4.1.9)
|
||||||
activesupport (>= 3.0, < 6.2)
|
activesupport (>= 3.0, < 6.2)
|
||||||
delayed_job_active_record (4.1.6)
|
delayed_job_active_record (4.1.6)
|
||||||
|
@ -195,6 +202,7 @@ GEM
|
||||||
equalizer (0.0.11)
|
equalizer (0.0.11)
|
||||||
erubi (1.10.0)
|
erubi (1.10.0)
|
||||||
eventmachine (1.2.7)
|
eventmachine (1.2.7)
|
||||||
|
eventmachine (1.2.7-x86_64-linux-musl)
|
||||||
execjs (2.8.1)
|
execjs (2.8.1)
|
||||||
factory_bot (6.2.0)
|
factory_bot (6.2.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
|
@ -226,7 +234,7 @@ GEM
|
||||||
faraday-rack (1.0.0)
|
faraday-rack (1.0.0)
|
||||||
faraday_middleware (1.0.0)
|
faraday_middleware (1.0.0)
|
||||||
faraday (~> 1.0)
|
faraday (~> 1.0)
|
||||||
ffi (1.15.3)
|
ffi (1.15.3-x86_64-linux-musl)
|
||||||
ffi-compiler (1.0.1)
|
ffi-compiler (1.0.1)
|
||||||
ffi (>= 1.0.0)
|
ffi (>= 1.0.0)
|
||||||
rake
|
rake
|
||||||
|
@ -242,7 +250,7 @@ GEM
|
||||||
rainbow (>= 2.2.1)
|
rainbow (>= 2.2.1)
|
||||||
rake (>= 10.0)
|
rake (>= 10.0)
|
||||||
gli (2.20.1)
|
gli (2.20.1)
|
||||||
globalid (0.5.2)
|
globalid (1.0.0)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 5.0)
|
||||||
gmail_xoauth (0.4.2)
|
gmail_xoauth (0.4.2)
|
||||||
oauth (>= 0.3.6)
|
oauth (>= 0.3.6)
|
||||||
|
@ -267,6 +275,7 @@ GEM
|
||||||
hashdiff (1.0.1)
|
hashdiff (1.0.1)
|
||||||
hashie (4.1.0)
|
hashie (4.1.0)
|
||||||
hiredis (0.6.3)
|
hiredis (0.6.3)
|
||||||
|
hiredis (0.6.3-x86_64-linux-musl)
|
||||||
htmlentities (4.3.4)
|
htmlentities (4.3.4)
|
||||||
http (4.4.1)
|
http (4.4.1)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
|
@ -277,11 +286,12 @@ GEM
|
||||||
http-cookie (1.0.4)
|
http-cookie (1.0.4)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
http-form_data (2.3.0)
|
http-form_data (2.3.0)
|
||||||
http-parser (1.2.3)
|
http-parser (1.2.3-x86_64-linux-musl)
|
||||||
ffi-compiler (>= 1.0, < 2.0)
|
ffi-compiler (>= 1.0, < 2.0)
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.6.0)
|
||||||
|
http_parser.rb (0.6.0-x86_64-linux-musl)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.8.10)
|
i18n (1.10.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
icalendar (2.7.1)
|
icalendar (2.7.1)
|
||||||
ice_cube (~> 0.16)
|
ice_cube (~> 0.16)
|
||||||
|
@ -293,13 +303,15 @@ GEM
|
||||||
iniparse (1.5.0)
|
iniparse (1.5.0)
|
||||||
interception (0.5)
|
interception (0.5)
|
||||||
json (2.5.1)
|
json (2.5.1)
|
||||||
|
json (2.5.1-x86_64-linux-musl)
|
||||||
jwt (2.2.3)
|
jwt (2.2.3)
|
||||||
kgio (2.11.4)
|
kgio (2.11.4)
|
||||||
|
kgio (2.11.4-x86_64-linux-musl)
|
||||||
koala (3.0.0)
|
koala (3.0.0)
|
||||||
addressable
|
addressable
|
||||||
faraday
|
faraday
|
||||||
json (>= 1.8)
|
json (>= 1.8)
|
||||||
libv8 (8.4.255.0)
|
libv8 (8.4.255.0-x86_64-linux)
|
||||||
listen (3.5.1)
|
listen (3.5.1)
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
|
@ -307,11 +319,11 @@ GEM
|
||||||
logging (2.3.0)
|
logging (2.3.0)
|
||||||
little-plugger (~> 1.1)
|
little-plugger (~> 1.1)
|
||||||
multi_json (~> 1.14)
|
multi_json (~> 1.14)
|
||||||
loofah (2.12.0)
|
loofah (2.14.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
lumberjack (1.2.8)
|
lumberjack (1.2.8)
|
||||||
marcel (1.0.1)
|
marcel (1.0.2)
|
||||||
memoizable (0.4.2)
|
memoizable (0.4.2)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
messagebird-rest (3.0.0)
|
messagebird-rest (3.0.0)
|
||||||
|
@ -319,24 +331,30 @@ GEM
|
||||||
mime-types (3.3.1)
|
mime-types (3.3.1)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2021.0225)
|
mime-types-data (3.2021.0225)
|
||||||
mini_mime (1.1.1)
|
mini_mime (1.1.2)
|
||||||
mini_portile2 (2.6.1)
|
mini_portile2 (2.8.0)
|
||||||
mini_racer (0.2.9)
|
mini_racer (0.2.9)
|
||||||
libv8 (>= 6.9.411)
|
libv8 (>= 6.9.411)
|
||||||
minitest (5.14.4)
|
minitest (5.15.0)
|
||||||
msgpack (1.4.2)
|
msgpack (1.4.2)
|
||||||
|
msgpack (1.4.2-x86_64-linux-musl)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
mysql2 (0.5.3)
|
mysql2 (0.5.3)
|
||||||
|
mysql2 (0.5.3-x86_64-linux-musl)
|
||||||
naught (1.1.0)
|
naught (1.1.0)
|
||||||
nenv (0.3.0)
|
nenv (0.3.0)
|
||||||
nestful (1.1.4)
|
nestful (1.1.4)
|
||||||
net-ldap (0.17.0)
|
net-ldap (0.17.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.12.4)
|
nio4r (2.5.8-x86_64-linux-musl)
|
||||||
mini_portile2 (~> 2.6.1)
|
nokogiri (1.13.3)
|
||||||
|
mini_portile2 (~> 2.8.0)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.13.3-x86_64-linux-musl)
|
||||||
|
mini_portile2 (~> 2.8.0)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nori (2.6.0)
|
nori (2.6.0)
|
||||||
notiffany (0.1.3)
|
notiffany (0.1.3)
|
||||||
|
@ -392,7 +410,7 @@ GEM
|
||||||
omniauth (~> 1.5)
|
omniauth (~> 1.5)
|
||||||
omniauth-oauth2 (>= 1.4.0)
|
omniauth-oauth2 (>= 1.4.0)
|
||||||
openssl (2.2.0)
|
openssl (2.2.0)
|
||||||
os (1.1.1)
|
openssl (2.2.0-x86_64-linux-musl)
|
||||||
overcommit (0.58.0)
|
overcommit (0.58.0)
|
||||||
childprocess (>= 0.6.3, < 5)
|
childprocess (>= 0.6.3, < 5)
|
||||||
iniparse (~> 1.4)
|
iniparse (~> 1.4)
|
||||||
|
@ -401,6 +419,7 @@ GEM
|
||||||
parser (3.0.2.0)
|
parser (3.0.2.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
pg (0.21.0)
|
pg (0.21.0)
|
||||||
|
pg (0.21.0-x86_64-linux-musl)
|
||||||
power_assert (2.0.1)
|
power_assert (2.0.1)
|
||||||
protocol-hpack (1.4.2)
|
protocol-hpack (1.4.2)
|
||||||
protocol-http (0.22.4)
|
protocol-http (0.22.4)
|
||||||
|
@ -424,32 +443,35 @@ GEM
|
||||||
binding_of_caller (~> 1.0)
|
binding_of_caller (~> 1.0)
|
||||||
pry (~> 0.13)
|
pry (~> 0.13)
|
||||||
public_suffix (4.0.6)
|
public_suffix (4.0.6)
|
||||||
puma (4.3.8)
|
puma (4.3.11)
|
||||||
|
nio4r (~> 2.0)
|
||||||
|
puma (4.3.8-x86_64-linux-musl)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
pundit (2.1.1)
|
pundit (2.1.1)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
pundit-matchers (1.7.0)
|
pundit-matchers (1.7.0)
|
||||||
rspec-rails (>= 3.0.0)
|
rspec-rails (>= 3.0.0)
|
||||||
racc (1.5.2)
|
racc (1.6.0)
|
||||||
|
racc (1.6.0-x86_64-linux-musl)
|
||||||
rack (2.2.3)
|
rack (2.2.3)
|
||||||
rack-livereload (0.3.17)
|
rack-livereload (0.3.17)
|
||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rails (6.0.4.1)
|
rails (6.0.4.7)
|
||||||
actioncable (= 6.0.4.1)
|
actioncable (= 6.0.4.7)
|
||||||
actionmailbox (= 6.0.4.1)
|
actionmailbox (= 6.0.4.7)
|
||||||
actionmailer (= 6.0.4.1)
|
actionmailer (= 6.0.4.7)
|
||||||
actionpack (= 6.0.4.1)
|
actionpack (= 6.0.4.7)
|
||||||
actiontext (= 6.0.4.1)
|
actiontext (= 6.0.4.7)
|
||||||
actionview (= 6.0.4.1)
|
actionview (= 6.0.4.7)
|
||||||
activejob (= 6.0.4.1)
|
activejob (= 6.0.4.7)
|
||||||
activemodel (= 6.0.4.1)
|
activemodel (= 6.0.4.7)
|
||||||
activerecord (= 6.0.4.1)
|
activerecord (= 6.0.4.7)
|
||||||
activestorage (= 6.0.4.1)
|
activestorage (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 6.0.4.1)
|
railties (= 6.0.4.7)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.5)
|
rails-controller-testing (1.0.5)
|
||||||
actionpack (>= 5.0.1.rc1)
|
actionpack (>= 5.0.1.rc1)
|
||||||
|
@ -458,16 +480,17 @@ GEM
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.4.1)
|
rails-html-sanitizer (1.4.2)
|
||||||
loofah (~> 2.3)
|
loofah (~> 2.3)
|
||||||
railties (6.0.4.1)
|
railties (6.0.4.7)
|
||||||
actionpack (= 6.0.4.1)
|
actionpack (= 6.0.4.7)
|
||||||
activesupport (= 6.0.4.1)
|
activesupport (= 6.0.4.7)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.20.3, < 2.0)
|
thor (>= 0.20.3, < 2.0)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
raindrops (0.19.2)
|
raindrops (0.19.2)
|
||||||
|
raindrops (0.19.2-x86_64-linux-musl)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
rb-fsevent (0.11.0)
|
rb-fsevent (0.11.0)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
|
@ -499,6 +522,7 @@ GEM
|
||||||
rspec-support (~> 3.10)
|
rspec-support (~> 3.10)
|
||||||
rspec-support (3.10.2)
|
rspec-support (3.10.2)
|
||||||
rszr (0.5.2)
|
rszr (0.5.2)
|
||||||
|
rszr (0.5.2-x86_64-linux-musl)
|
||||||
rubocop (1.21.0)
|
rubocop (1.21.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.0.0.0)
|
parser (>= 3.0.0.0)
|
||||||
|
@ -531,6 +555,8 @@ GEM
|
||||||
rubyzip (2.3.0)
|
rubyzip (2.3.0)
|
||||||
sassc (2.4.0)
|
sassc (2.4.0)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
|
sassc (2.4.0-x86_64-linux-musl)
|
||||||
|
ffi (~> 1.9)
|
||||||
sassc-rails (2.1.2)
|
sassc-rails (2.1.2)
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
sassc (>= 2.0)
|
sassc (>= 2.0)
|
||||||
|
@ -571,9 +597,9 @@ GEM
|
||||||
sprockets (3.7.2)
|
sprockets (3.7.2)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (> 1, < 3)
|
||||||
sprockets-rails (3.2.2)
|
sprockets-rails (3.4.2)
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 5.2)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 5.2)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sync (0.5.0)
|
sync (0.5.0)
|
||||||
tcr (0.2.2)
|
tcr (0.2.2)
|
||||||
|
@ -584,7 +610,7 @@ GEM
|
||||||
tins (~> 1.0)
|
tins (~> 1.0)
|
||||||
test-unit (3.4.7)
|
test-unit (3.4.7)
|
||||||
power_assert
|
power_assert
|
||||||
thor (1.1.0)
|
thor (1.2.1)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tilt (2.0.10)
|
tilt (2.0.10)
|
||||||
timers (4.3.3)
|
timers (4.3.3)
|
||||||
|
@ -611,11 +637,14 @@ GEM
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.7.7)
|
unf_ext (0.0.7.7-x86_64-linux-musl)
|
||||||
unicode-display_width (2.1.0)
|
unicode-display_width (2.1.0)
|
||||||
unicorn (6.0.0)
|
unicorn (6.0.0)
|
||||||
kgio (~> 2.6)
|
kgio (~> 2.6)
|
||||||
raindrops (~> 0.7)
|
raindrops (~> 0.7)
|
||||||
|
unicorn (6.0.0-x86_64-linux-musl)
|
||||||
|
kgio (~> 2.6)
|
||||||
|
raindrops (~> 0.7)
|
||||||
valid_email2 (4.0.0)
|
valid_email2 (4.0.0)
|
||||||
activemodel (>= 3.2)
|
activemodel (>= 3.2)
|
||||||
mail (~> 2.5)
|
mail (~> 2.5)
|
||||||
|
@ -631,11 +660,13 @@ GEM
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
websocket-driver (0.7.5)
|
websocket-driver (0.7.5)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
|
websocket-driver (0.7.5-x86_64-linux-musl)
|
||||||
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
writeexcel (1.0.5)
|
writeexcel (1.0.5)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
zeitwerk (2.4.2)
|
zeitwerk (2.5.4)
|
||||||
zendesk_api (1.33.0)
|
zendesk_api (1.33.0)
|
||||||
faraday (>= 0.9.0, < 2.0.0)
|
faraday (>= 0.9.0, < 2.0.0)
|
||||||
hashie (>= 3.5.2, < 5.0.0)
|
hashie (>= 3.5.2, < 5.0.0)
|
||||||
|
@ -645,6 +676,7 @@ GEM
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
x86_64-linux-musl
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
aasm
|
aasm
|
||||||
|
@ -711,7 +743,6 @@ DEPENDENCIES
|
||||||
omniauth-twitter
|
omniauth-twitter
|
||||||
omniauth-weibo-oauth2
|
omniauth-weibo-oauth2
|
||||||
openssl
|
openssl
|
||||||
os
|
|
||||||
overcommit
|
overcommit
|
||||||
pg (= 0.21.0)
|
pg (= 0.21.0)
|
||||||
pry-rails
|
pry-rails
|
||||||
|
@ -761,7 +792,7 @@ DEPENDENCIES
|
||||||
zendesk_api
|
zendesk_api
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
ruby 2.7.4p191
|
ruby 2.7.5p203
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.2.20
|
2.2.20
|
||||||
|
|
149
Makefile
Normal file
149
Makefile
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
SHELL := /bin/bash
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
# Copiar el archivo de configuración y avisar cuando hay que
|
||||||
|
# actualizarlo.
|
||||||
|
.env: .env.example
|
||||||
|
@test -f $@ || cp -v $< $@
|
||||||
|
@test -f $@ && echo "Revisa $@ para actualizarlo con respecto a $<"
|
||||||
|
@test -f $@ && diff -auN --color $@ $<
|
||||||
|
|
||||||
|
include .env
|
||||||
|
|
||||||
|
export
|
||||||
|
|
||||||
|
# XXX: El espacio antes del comentario cuenta como espacio
|
||||||
|
args ?=## Argumentos para Hain
|
||||||
|
commit ?= origin/rails## Commit desde el que actualizar
|
||||||
|
env ?= staging## Entorno del nodo delegado
|
||||||
|
sutty ?= $(SUTTY)## Dirección local
|
||||||
|
delegate ?= $(DELEGATE)## Cambia el nodo delegado
|
||||||
|
hain ?= $(HAINISH)## Ubicación de Hainish
|
||||||
|
|
||||||
|
# El nodo delegado tiene dos entornos, production y staging.
|
||||||
|
# Dependiendo del entorno que elijamos, se van a generar los assets y el
|
||||||
|
# contenedor y subirse a un servidor u otro. No utilizamos CI/CD (aún).
|
||||||
|
#
|
||||||
|
# Production es el entorno de panel.sutty.nl
|
||||||
|
ifeq ($(env),production)
|
||||||
|
container ?= sutty
|
||||||
|
## TODO: Cambiar a otra cosa
|
||||||
|
branch ?= rails
|
||||||
|
public ?= public
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Staging es el entorno de panel.staging.sutty.nl
|
||||||
|
ifeq ($(env),staging)
|
||||||
|
container := staging
|
||||||
|
branch := staging
|
||||||
|
public := staging
|
||||||
|
endif
|
||||||
|
|
||||||
|
help: always ## Ayuda
|
||||||
|
@echo -e "Sutty\n" | sed -re "s/^.*/\x1B[38;5;197m&\x1B[0m/"
|
||||||
|
@echo -e "Servidor: https://panel.$(SUTTY_WITH_PORT)/\n"
|
||||||
|
@echo -e "Uso: make TAREA args=\"ARGUMENTOS\"\n"
|
||||||
|
@echo -e "Tareas:\n"
|
||||||
|
@grep -E "^[a-z\-]+:.*##" Makefile | sed -re "s/(.*):.*##(.*)/\1;\2/" | column -s ";" -t | sed -re "s/^([^ ]+) /\x1B[38;5;197m\1\x1B[0m/"
|
||||||
|
@echo -e "\nArgumentos:\n"
|
||||||
|
@grep -E "^[a-z\-]+ \?=.*##" Makefile | sed -re "s/(.*) \?=.*##(.*)/\1;\2/" | column -s ";" -t | sed -re "s/^([^ ]+) /\x1B[38;5;197m\1\x1B[0m/"
|
||||||
|
|
||||||
|
assets: node_modules public/packs/manifest.json.br ## Compilar los assets
|
||||||
|
|
||||||
|
test: always ## Ejecutar los tests
|
||||||
|
$(MAKE) rake args="test RAILS_ENV=test $(args)"
|
||||||
|
|
||||||
|
postgresql: /etc/hosts ## Iniciar la base de datos
|
||||||
|
pgrep postgres >/dev/null || $(hain) postgresql
|
||||||
|
|
||||||
|
serve-js: /etc/hosts node_modules ## Iniciar el servidor de desarrollo de Javascript
|
||||||
|
$(hain) 'bundle exec ./bin/webpack-dev-server'
|
||||||
|
|
||||||
|
serve: /etc/hosts postgresql Gemfile.lock ## Iniciar el servidor de desarrollo de Rails
|
||||||
|
$(MAKE) rails args=server
|
||||||
|
|
||||||
|
rails: ## Corre rails dentro del entorno de desarrollo (pasar argumentos con args=).
|
||||||
|
$(MAKE) bundle args="exec rails $(args)"
|
||||||
|
|
||||||
|
rake: ## Corre rake dentro del entorno de desarrollo (pasar argumentos con args=).
|
||||||
|
$(MAKE) bundle args="exec rake $(args)"
|
||||||
|
|
||||||
|
bundle: ## Corre bundle dentro del entorno de desarrollo (pasar argumentos con args=).
|
||||||
|
$(hain) 'bundle $(args)'
|
||||||
|
|
||||||
|
rubocop: ## Yutea el código que está por ser commiteado
|
||||||
|
git status --porcelain \
|
||||||
|
| grep -E "^(A|M)" \
|
||||||
|
| sed "s/^...//" \
|
||||||
|
| grep ".rb$$" \
|
||||||
|
| ../haini.sh/haini.sh "xargs -r ./bin/rubocop --auto-correct"
|
||||||
|
|
||||||
|
audit: ## Encuentra dependencias con vulnerabilidades
|
||||||
|
$(hain) 'gem install bundler-audit'
|
||||||
|
$(hain) 'bundle audit --update'
|
||||||
|
|
||||||
|
brakeman: ## Busca posibles vulnerabilidades en Sutty
|
||||||
|
$(MAKE) bundle args='exec brakeman'
|
||||||
|
|
||||||
|
yarn: ## Tareas de yarn
|
||||||
|
$(hain) 'yarn $(args)'
|
||||||
|
|
||||||
|
clean: ## Limpieza
|
||||||
|
rm -rf _sites/test-* _deploy/test-* log/*.log tmp/cache tmp/letter_opener tmp/miniprofiler tmp/storage
|
||||||
|
|
||||||
|
build: Gemfile.lock ## Generar la imagen Docker
|
||||||
|
time docker build --build-arg="BRANCH=$(branch)" --build-arg="RAILS_MASTER_KEY=`cat config/master.key`" -t sutty/$(container) .
|
||||||
|
docker tag sutty/$(container):latest sutty:keep
|
||||||
|
@echo -e "\a"
|
||||||
|
|
||||||
|
save: ## Subir la imagen Docker al nodo delegado
|
||||||
|
time docker save sutty/$(container):latest | ssh root@$(delegate) docker load
|
||||||
|
date +%F | xargs -I {} git tag -f $(container)-{}
|
||||||
|
@echo -e "\a"
|
||||||
|
|
||||||
|
ota-js: assets ## Actualizar Javascript en el nodo delegado
|
||||||
|
sudo chgrp -R 82 public/
|
||||||
|
rsync -avi --delete-after public/ root@$(delegate):/srv/sutty/srv/http/data/_$(public)/
|
||||||
|
ssh root@$(delegate) docker exec $(container) sh -c "cat /srv/http/tmp/puma.pid | xargs -r kill -USR2"
|
||||||
|
|
||||||
|
ota: ## Actualizar Rails en el nodo delegado
|
||||||
|
umask 022; git format-patch $(commit)
|
||||||
|
scp ./0*.patch $(delegate):/tmp/
|
||||||
|
ssh $(delegate) mkdir -p /tmp/patches-$(commit)/
|
||||||
|
scp ./0*.patch $(delegate):/tmp/patches-$(commit)/
|
||||||
|
scp ./ota.sh $(delegate):/tmp/
|
||||||
|
ssh $(delegate) docker cp /tmp/patches-$(shell echo $(commit) | cut -d / -f 1) $(container):/tmp/
|
||||||
|
ssh $(delegate) docker cp /tmp/ota.sh $(container):/usr/local/bin/ota
|
||||||
|
ssh $(delegate) docker exec $(container) apk add --no-cache patch
|
||||||
|
ssh $(delegate) docker exec $(container) ota $(commit)
|
||||||
|
rm ./0*.patch
|
||||||
|
|
||||||
|
# Todos los archivos de assets. Si alguno cambia, se van a recompilar
|
||||||
|
# los assets que luego se suben al nodo delegado.
|
||||||
|
assets := package.json yarn.lock $(shell find app/assets/ app/javascript/ -type f)
|
||||||
|
public/packs/manifest.json.br: $(assets)
|
||||||
|
$(hain) 'PANEL_URL=https://panel.sutty.nl RAILS_ENV=production NODE_ENV=production bundle exec rake assets:precompile assets:clean'
|
||||||
|
|
||||||
|
# Correr un test en particular por ejemplo
|
||||||
|
# `make test/models/usuarie_test.rb`
|
||||||
|
tests := $(shell find test/ -name "*_test.rb")
|
||||||
|
$(tests): always
|
||||||
|
$(MAKE) test args="TEST=$@"
|
||||||
|
|
||||||
|
# Agrega las direcciones locales al sistema
|
||||||
|
/etc/hosts: always
|
||||||
|
@echo "Chequeando si es necesario agregar el dominio local $(SUTTY)"
|
||||||
|
@grep -q " $(SUTTY)$$" $@ || echo -e "127.0.0.1 $(SUTTY)\n::1 $(SUTTY)" | sudo tee -a $@
|
||||||
|
@grep -q " api.$(SUTTY)$$" $@ || echo -e "127.0.0.1 api.$(SUTTY)\n::1 api.$(SUTTY)" | sudo tee -a $@
|
||||||
|
@grep -q " panel.$(SUTTY)$$" $@ || echo -e "127.0.0.1 panel.$(SUTTY)\n::1 panel.$(SUTTY)" | sudo tee -a $@
|
||||||
|
@grep -q " postgresql.$(SUTTY)$$" $@ || echo -e "127.0.0.1 postgresql.$(SUTTY)\n::1 postgresql.$(SUTTY)" | sudo tee -a $@
|
||||||
|
|
||||||
|
# Instala las dependencias de Javascript
|
||||||
|
node_modules: package.json
|
||||||
|
$(MAKE) yarn
|
||||||
|
|
||||||
|
# Instala las dependencias de Rails
|
||||||
|
Gemfile.lock: Gemfile
|
||||||
|
$(MAKE) bundle args=install
|
||||||
|
|
||||||
|
.PHONY: always
|
|
@ -300,9 +300,10 @@ class App.Controller extends Spine.Controller
|
||||||
|
|
||||||
frontendTimeUpdateItem: (item, currentVal) =>
|
frontendTimeUpdateItem: (item, currentVal) =>
|
||||||
timestamp = item.attr('datetime')
|
timestamp = item.attr('datetime')
|
||||||
time = @humanTime(timestamp, item.hasClass('escalation'))
|
return if timestamp is 'null'
|
||||||
|
|
||||||
# only do dom updates on changes
|
# only do dom updates on changes
|
||||||
|
time = @humanTime(timestamp, item.hasClass('escalation'))
|
||||||
return if time is currentVal
|
return if time is currentVal
|
||||||
|
|
||||||
newTitle = App.i18n.translateTimestamp(timestamp)
|
newTitle = App.i18n.translateTimestamp(timestamp)
|
||||||
|
|
|
@ -418,11 +418,11 @@ class App.ControllerForm extends App.Controller
|
||||||
|
|
||||||
if !@constructor.fieldIsMandatory(field_by_name)
|
if !@constructor.fieldIsMandatory(field_by_name)
|
||||||
field_by_name.attr('required', true)
|
field_by_name.attr('required', true)
|
||||||
field_by_name.parents('.form-group').find('label span').html('*')
|
field_by_name.closest('.form-group').find('label span').html('*')
|
||||||
field_by_name.closest('.form-group').addClass('is-required')
|
field_by_name.closest('.form-group').addClass('is-required')
|
||||||
if !@constructor.fieldIsMandatory(field_by_data)
|
if !@constructor.fieldIsMandatory(field_by_data)
|
||||||
field_by_data.attr('required', true)
|
field_by_data.attr('required', true)
|
||||||
field_by_data.parents('.form-group').find('label span').html('*')
|
field_by_data.closest('.form-group').find('label span').html('*')
|
||||||
field_by_data.closest('.form-group').addClass('is-required')
|
field_by_data.closest('.form-group').addClass('is-required')
|
||||||
|
|
||||||
optional: (name, el = @form) ->
|
optional: (name, el = @form) ->
|
||||||
|
@ -434,11 +434,11 @@ class App.ControllerForm extends App.Controller
|
||||||
|
|
||||||
if @constructor.fieldIsMandatory(field_by_name)
|
if @constructor.fieldIsMandatory(field_by_name)
|
||||||
field_by_name.attr('required', false)
|
field_by_name.attr('required', false)
|
||||||
field_by_name.parents('.form-group').find('label span').html('')
|
field_by_name.closest('.form-group').find('label span').html('')
|
||||||
field_by_name.closest('.form-group').removeClass('is-required')
|
field_by_name.closest('.form-group').removeClass('is-required')
|
||||||
if @constructor.fieldIsMandatory(field_by_data)
|
if @constructor.fieldIsMandatory(field_by_data)
|
||||||
field_by_data.attr('required', false)
|
field_by_data.attr('required', false)
|
||||||
field_by_data.parents('.form-group').find('label span').html('')
|
field_by_data.closest('.form-group').find('label span').html('')
|
||||||
field_by_data.closest('.form-group').removeClass('is-required')
|
field_by_data.closest('.form-group').removeClass('is-required')
|
||||||
|
|
||||||
readonly: (name, el = @form) ->
|
readonly: (name, el = @form) ->
|
||||||
|
|
|
@ -2,6 +2,7 @@ class App.ControllerObserver extends App.Controller
|
||||||
model: 'Ticket'
|
model: 'Ticket'
|
||||||
template: 'tba'
|
template: 'tba'
|
||||||
globalRerender: true
|
globalRerender: true
|
||||||
|
lastAttributes: undefined
|
||||||
|
|
||||||
###
|
###
|
||||||
observe:
|
observe:
|
||||||
|
@ -25,7 +26,7 @@ class App.ControllerObserver extends App.Controller
|
||||||
# rerender, e. g. on language change
|
# rerender, e. g. on language change
|
||||||
if @globalRerender
|
if @globalRerender
|
||||||
@controllerBind('ui:rerender', =>
|
@controllerBind('ui:rerender', =>
|
||||||
@lastAttributres = undefined
|
@lastAttributes = undefined
|
||||||
@maybeRender(App[@model].fullLocal(@object_id))
|
@maybeRender(App[@model].fullLocal(@object_id))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,32 +44,40 @@ class App.ControllerObserver extends App.Controller
|
||||||
if !@subscribeId
|
if !@subscribeId
|
||||||
@subscribeId = object.subscribe(@subscribe)
|
@subscribeId = object.subscribe(@subscribe)
|
||||||
|
|
||||||
# remember current attributes
|
return if !@hasChanged(object)
|
||||||
|
|
||||||
|
@render(object)
|
||||||
|
|
||||||
|
hasChanged: (object) =>
|
||||||
currentAttributes = {}
|
currentAttributes = {}
|
||||||
|
|
||||||
|
objectCloned = $.extend(true, {}, object)
|
||||||
if @observe
|
if @observe
|
||||||
for key, active of @observe
|
for key, active of @observe
|
||||||
if active
|
if active && !_.isFunction(value)
|
||||||
currentAttributes[key] = object[key]
|
currentAttributes[key] = objectCloned[key]
|
||||||
|
|
||||||
if @observeNot
|
if @observeNot
|
||||||
for key, value of object
|
for key, value of objectCloned
|
||||||
if key isnt 'cid' && !@observeNot[key] && !_.isFunction(value) && !_.isObject(value)
|
if key isnt 'cid' && !@observeNot[key] && !_.isFunction(value)
|
||||||
currentAttributes[key] = value
|
currentAttributes[key] = value
|
||||||
|
|
||||||
if !@lastAttributres
|
if !@lastAttributes
|
||||||
@lastAttributres = {}
|
@lastAttributes = currentAttributes
|
||||||
else
|
return true
|
||||||
diff = difference(currentAttributes, @lastAttributres)
|
|
||||||
if _.isEmpty(diff)
|
diff = difference(currentAttributes, @lastAttributes)
|
||||||
@log 'debug', 'maybeRender no diff, no rerender'
|
if _.isEmpty(diff)
|
||||||
return
|
@log 'debug', 'maybeRender no diff, no rerender'
|
||||||
|
return false
|
||||||
|
|
||||||
@log 'debug', 'maybeRender.diff', diff, @observe, @model
|
@log 'debug', 'maybeRender.diff', diff, @observe, @model
|
||||||
@lastAttributres = currentAttributes
|
@lastAttributes = currentAttributes
|
||||||
|
|
||||||
@render(object, diff)
|
true
|
||||||
|
|
||||||
render: (object, diff) =>
|
render: (object) =>
|
||||||
@log 'debug', 'render', @template, object, diff
|
@log 'debug', 'render', @template, object
|
||||||
@html App.view(@template)(
|
@html App.view(@template)(
|
||||||
object: object
|
object: object
|
||||||
)
|
)
|
||||||
|
|
|
@ -493,6 +493,15 @@ class App.ControllerTable extends App.Controller
|
||||||
sortable: @dndCallback
|
sortable: @dndCallback
|
||||||
))
|
))
|
||||||
|
|
||||||
|
sortObjectKeys: (objects, direction) ->
|
||||||
|
sorted = Object.keys(objects).sort()
|
||||||
|
|
||||||
|
switch direction
|
||||||
|
when 'DESC'
|
||||||
|
sorted.reverse()
|
||||||
|
else
|
||||||
|
sorted
|
||||||
|
|
||||||
renderTableRows: (sort = false) =>
|
renderTableRows: (sort = false) =>
|
||||||
if sort is true
|
if sort is true
|
||||||
@sortList()
|
@sortList()
|
||||||
|
@ -506,11 +515,11 @@ class App.ControllerTable extends App.Controller
|
||||||
objectsToShow = @objectsOfPage(@pagerShownPage)
|
objectsToShow = @objectsOfPage(@pagerShownPage)
|
||||||
if @groupBy
|
if @groupBy
|
||||||
# group by raw (and not printable) value so dates work also
|
# group by raw (and not printable) value so dates work also
|
||||||
objectsGrouped = _.groupBy(objectsToShow, (object) => object[@groupBy])
|
objectsGrouped = _.groupBy(objectsToShow, (object) => @groupObjectName(object, @groupBy, excludeTags: ['date', 'datetime']))
|
||||||
else
|
else
|
||||||
objectsGrouped = { '': objectsToShow }
|
objectsGrouped = { '': objectsToShow }
|
||||||
|
|
||||||
for groupValue in Object.keys(objectsGrouped).sort()
|
for groupValue in @sortObjectKeys(objectsGrouped, @groupDirection)
|
||||||
groupObjects = objectsGrouped[groupValue]
|
groupObjects = objectsGrouped[groupValue]
|
||||||
|
|
||||||
for object in groupObjects
|
for object in groupObjects
|
||||||
|
@ -846,11 +855,15 @@ class App.ControllerTable extends App.Controller
|
||||||
@objects = localObjects
|
@objects = localObjects
|
||||||
@lastSortedobjects = localObjects
|
@lastSortedobjects = localObjects
|
||||||
|
|
||||||
groupObjectName: (object, key = undefined) ->
|
groupObjectName: (object, key = undefined, options = {}) ->
|
||||||
group = object
|
group = object
|
||||||
if key
|
if key
|
||||||
if key not of object
|
if key not of object
|
||||||
key += '_id'
|
key += '_id'
|
||||||
|
|
||||||
|
# return internal value if needed
|
||||||
|
return object[key] if options.excludeTags && _.find(@attributesList, (attr) -> attr.name == key && _.contains(options.excludeTags, attr.tag))
|
||||||
|
|
||||||
group = App.viewPrint(object, key, @attributesList)
|
group = App.viewPrint(object, key, @attributesList)
|
||||||
if _.isEmpty(group)
|
if _.isEmpty(group)
|
||||||
group = ''
|
group = ''
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
class App.ControllerTechnicalErrorModal extends App.ControllerModal
|
||||||
|
head: "StatusCode: #{status}"
|
||||||
|
contentCode: ''
|
||||||
|
buttonClose: false
|
||||||
|
buttonSubmit: 'Ok'
|
||||||
|
onSubmit: (e) -> @close(e)
|
||||||
|
|
||||||
|
content: ->
|
||||||
|
"<pre><code>#{@contentCode}</code></pre>"
|
|
@ -428,20 +428,7 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
|
||||||
ui.hide('options::folder')
|
ui.hide('options::folder')
|
||||||
ui.hide('options::keep_on_server')
|
ui.hide('options::keep_on_server')
|
||||||
|
|
||||||
handlePort = (params, attribute, attributes, classname, form, ui) ->
|
form = new App.ControllerForm(
|
||||||
return if !params
|
|
||||||
return if !params.options
|
|
||||||
currentPort = @$('[name="options::port"]').val()
|
|
||||||
if params.options.ssl is true
|
|
||||||
if !currentPort || currentPort is '143'
|
|
||||||
@$('[name="options::port"]').val('993')
|
|
||||||
return
|
|
||||||
if params.options.ssl is false
|
|
||||||
if !currentPort || currentPort is '993'
|
|
||||||
@$('[name="options::port"]').val('143')
|
|
||||||
return
|
|
||||||
|
|
||||||
new App.ControllerForm(
|
|
||||||
el: @$('.base-inbound-settings'),
|
el: @$('.base-inbound-settings'),
|
||||||
model:
|
model:
|
||||||
configure_attributes: configureAttributesInbound
|
configure_attributes: configureAttributesInbound
|
||||||
|
@ -449,10 +436,16 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
|
||||||
params: @account.inbound
|
params: @account.inbound
|
||||||
handlers: [
|
handlers: [
|
||||||
showHideFolder,
|
showHideFolder,
|
||||||
handlePort,
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
form.el.find("select[name='options::ssl']").off('change').on('change', (e) ->
|
||||||
|
if $(e.target).val() is 'true'
|
||||||
|
form.el.find("[name='options::port']").val('993')
|
||||||
|
else
|
||||||
|
form.el.find("[name='options::port']").val('143')
|
||||||
|
)
|
||||||
|
|
||||||
toggleOutboundAdapter: =>
|
toggleOutboundAdapter: =>
|
||||||
|
|
||||||
# fill user / password based on intro info
|
# fill user / password based on intro info
|
||||||
|
|
|
@ -60,6 +60,30 @@ class Form extends App.Controller
|
||||||
placetel_token: App.Setting.get('placetel_token')
|
placetel_token: App.Setting.get('placetel_token')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
configure_attributes = [
|
||||||
|
{
|
||||||
|
name: 'view_limit',
|
||||||
|
display: '',
|
||||||
|
tag: 'select',
|
||||||
|
null: false,
|
||||||
|
options: [
|
||||||
|
{ name: 60, value: 60 }
|
||||||
|
{ name: 120, value: 120 }
|
||||||
|
{ name: 180, value: 180 }
|
||||||
|
{ name: 240, value: 240 }
|
||||||
|
{ name: 300, value: 300 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
new App.ControllerForm(
|
||||||
|
el: @$('.js-viewLimit')
|
||||||
|
model:
|
||||||
|
configure_attributes: configure_attributes,
|
||||||
|
params:
|
||||||
|
view_limit: @config['view_limit']
|
||||||
|
autofocus: false
|
||||||
|
)
|
||||||
|
|
||||||
updateCurrentConfig: =>
|
updateCurrentConfig: =>
|
||||||
config = @config
|
config = @config
|
||||||
cleanupInput = @cleanupInput
|
cleanupInput = @cleanupInput
|
||||||
|
@ -70,6 +94,10 @@ class Form extends App.Controller
|
||||||
default_caller_id = @$('input[name=default_caller_id]').val()
|
default_caller_id = @$('input[name=default_caller_id]').val()
|
||||||
config.outbound.default_caller_id = cleanupInput(default_caller_id)
|
config.outbound.default_caller_id = cleanupInput(default_caller_id)
|
||||||
|
|
||||||
|
# default view limit
|
||||||
|
view_limit = @$('select[name=view_limit]').val()
|
||||||
|
config.view_limit = parseInt(view_limit)
|
||||||
|
|
||||||
# routing table
|
# routing table
|
||||||
config.outbound.routing_table = []
|
config.outbound.routing_table = []
|
||||||
@$('.js-outboundRouting .js-row').each(->
|
@$('.js-outboundRouting .js-row').each(->
|
||||||
|
|
|
@ -59,17 +59,42 @@ class Form extends App.Controller
|
||||||
config: @config
|
config: @config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
configure_attributes = [
|
||||||
|
{
|
||||||
|
name: 'view_limit',
|
||||||
|
display: '',
|
||||||
|
tag: 'select',
|
||||||
|
null: false,
|
||||||
|
options: [
|
||||||
|
{ name: 60, value: 60 }
|
||||||
|
{ name: 120, value: 120 }
|
||||||
|
{ name: 180, value: 180 }
|
||||||
|
{ name: 240, value: 240 }
|
||||||
|
{ name: 300, value: 300 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
new App.ControllerForm(
|
||||||
|
el: @$('.js-viewLimit')
|
||||||
|
model:
|
||||||
|
configure_attributes: configure_attributes,
|
||||||
|
params:
|
||||||
|
view_limit: @config['view_limit']
|
||||||
|
autofocus: false
|
||||||
|
)
|
||||||
|
|
||||||
updateCurrentConfig: =>
|
updateCurrentConfig: =>
|
||||||
config = @config
|
config = @config
|
||||||
cleanupInput = @cleanupInput
|
cleanupInput = @cleanupInput
|
||||||
|
|
||||||
config.api_user = cleanupInput(@$('input[name=api_user]').val())
|
|
||||||
config.api_password = cleanupInput(@$('input[name=api_password]').val())
|
|
||||||
|
|
||||||
# default caller_id
|
# default caller_id
|
||||||
default_caller_id = @$('input[name=default_caller_id]').val()
|
default_caller_id = @$('input[name=default_caller_id]').val()
|
||||||
config.outbound.default_caller_id = cleanupInput(default_caller_id)
|
config.outbound.default_caller_id = cleanupInput(default_caller_id)
|
||||||
|
|
||||||
|
# default view limit
|
||||||
|
view_limit = @$('select[name=view_limit]').val()
|
||||||
|
config.view_limit = parseInt(view_limit)
|
||||||
|
|
||||||
# routing table
|
# routing table
|
||||||
config.outbound.routing_table = []
|
config.outbound.routing_table = []
|
||||||
@$('.js-outboundRouting .js-row').each(->
|
@$('.js-outboundRouting .js-row').each(->
|
||||||
|
|
|
@ -96,6 +96,8 @@ class App.UiElement.ApplicationSelector
|
||||||
# ignore passwords and relations
|
# ignore passwords and relations
|
||||||
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids' && row.searchable isnt false
|
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids' && row.searchable isnt false
|
||||||
config = _.clone(row)
|
config = _.clone(row)
|
||||||
|
if config.tag is 'textarea'
|
||||||
|
config.expanding = false
|
||||||
if config.type is 'email' || config.type is 'tel'
|
if config.type is 'email' || config.type is 'tel'
|
||||||
config.type = 'text'
|
config.type = 'text'
|
||||||
for operatorRegEx, operator of operators_type
|
for operatorRegEx, operator of operators_type
|
||||||
|
|
|
@ -46,7 +46,7 @@ class App.UiElement.ApplicationUiElement
|
||||||
result = []
|
result = []
|
||||||
for row in selection
|
for row in selection
|
||||||
if attribute.translate
|
if attribute.translate
|
||||||
row.name = App.i18n.translateInline(row.name)
|
row.name = App.i18n.translatePlain(row.name)
|
||||||
if !_.isEmpty(row.children)
|
if !_.isEmpty(row.children)
|
||||||
row.children = @getConfigOptionListArray(attribute, row.children)
|
row.children = @getConfigOptionListArray(attribute, row.children)
|
||||||
result.push row
|
result.push row
|
||||||
|
@ -65,7 +65,7 @@ class App.UiElement.ApplicationUiElement
|
||||||
for key in order
|
for key in order
|
||||||
name_new = selection[key]
|
name_new = selection[key]
|
||||||
if attribute.translate
|
if attribute.translate
|
||||||
name_new = App.i18n.translateInline(name_new)
|
name_new = App.i18n.translatePlain(name_new)
|
||||||
attribute.options.push {
|
attribute.options.push {
|
||||||
name: name_new
|
name: name_new
|
||||||
value: key
|
value: key
|
||||||
|
@ -162,7 +162,7 @@ class App.UiElement.ApplicationUiElement
|
||||||
nameNew = item.name
|
nameNew = item.name
|
||||||
|
|
||||||
if attribute.translate
|
if attribute.translate
|
||||||
nameNew = App.i18n.translateInline(nameNew)
|
nameNew = App.i18n.translatePlain(nameNew)
|
||||||
|
|
||||||
row =
|
row =
|
||||||
value: item.id,
|
value: item.id,
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# coffeelint: disable=camel_case_classes
|
||||||
|
class App.UiElement.auth_provider
|
||||||
|
@render: (attribute) ->
|
||||||
|
for key, value of App.Config.get('auth_provider_all')
|
||||||
|
continue if value.config isnt attribute.provider
|
||||||
|
attribute.value = "#{App.Config.get('http_type')}://#{App.Config.get('fqdn')}#{value.url}/callback"
|
||||||
|
break
|
||||||
|
|
||||||
|
$( App.view('generic/auth_provider')( attribute: attribute ) )
|
|
@ -155,4 +155,4 @@ class App.UiElement.basedate
|
||||||
clear: 'clear'
|
clear: 'clear'
|
||||||
}
|
}
|
||||||
|
|
||||||
App.i18n.translateDeep(data)
|
App.i18n.translateDeepPlain(data)
|
||||||
|
|
|
@ -23,7 +23,7 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
|
||||||
organization:
|
organization:
|
||||||
name: 'Organization'
|
name: 'Organization'
|
||||||
model: 'Organization'
|
model: 'Organization'
|
||||||
model_show: ['Organization']
|
model_show: ['User', 'Organization']
|
||||||
'customer.organization':
|
'customer.organization':
|
||||||
name: 'Organization'
|
name: 'Organization'
|
||||||
model: 'Organization'
|
model: 'Organization'
|
||||||
|
@ -50,15 +50,15 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
|
||||||
|
|
||||||
operatorsType =
|
operatorsType =
|
||||||
'active$': ['is']
|
'active$': ['is']
|
||||||
'boolean$': ['is', 'is not', 'is set', 'not set']
|
'boolean$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
|
||||||
'integer$': ['is', 'is not', 'is set', 'not set']
|
'integer$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
|
||||||
'^select$': ['is', 'is not', 'is set', 'not set']
|
'^select$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
|
||||||
'^tree_select$': ['is', 'is not', 'is set', 'not set']
|
'^tree_select$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
|
||||||
'^(input|textarea|richtext)$': ['is', 'is not', 'is set', 'not set', 'regex match', 'regex mismatch']
|
'^(input|textarea|richtext)$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to', 'regex match', 'regex mismatch']
|
||||||
|
|
||||||
operatorsName =
|
operatorsName =
|
||||||
'_id$': ['is', 'is not', 'is set', 'not set']
|
'_id$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
|
||||||
'_ids$': ['is', 'is not', 'is set', 'not set']
|
'_ids$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
|
||||||
|
|
||||||
# merge config
|
# merge config
|
||||||
elements = {}
|
elements = {}
|
||||||
|
@ -153,6 +153,8 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
|
||||||
# ignore passwords and relations
|
# ignore passwords and relations
|
||||||
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids' && row.searchable isnt false
|
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids' && row.searchable isnt false
|
||||||
config = _.clone(row)
|
config = _.clone(row)
|
||||||
|
if config.tag is 'textarea'
|
||||||
|
config.expanding = false
|
||||||
if config.tag is 'select'
|
if config.tag is 'select'
|
||||||
config.multiple = true
|
config.multiple = true
|
||||||
config.default = undefined
|
config.default = undefined
|
||||||
|
@ -175,7 +177,7 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
|
||||||
currentOperator = elementRow.find('.js-operator option:selected').attr('value')
|
currentOperator = elementRow.find('.js-operator option:selected').attr('value')
|
||||||
name = @buildValueName(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
|
name = @buildValueName(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
|
||||||
|
|
||||||
if _.contains(['is set', 'not set'], currentOperator)
|
if _.contains(['is set', 'not set', 'has changed'], currentOperator)
|
||||||
elementRow.find('.js-value').addClass('hide').html('<input type="hidden" name="' + name + '" value="true" />')
|
elementRow.find('.js-value').addClass('hide').html('<input type="hidden" name="' + name + '" value="true" />')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
|
||||||
operatorsType =
|
operatorsType =
|
||||||
'boolean$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to']
|
'boolean$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to']
|
||||||
'integer$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly']
|
'integer$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly']
|
||||||
|
'^date': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly']
|
||||||
'^select$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
'^select$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
||||||
'^tree_select$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
'^tree_select$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
||||||
'^input$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'fill_in', 'fill_in_empty']
|
'^input$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'fill_in', 'fill_in_empty']
|
||||||
|
@ -63,8 +64,9 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for row in App[groupMeta.model].configure_attributes
|
for row in App[groupMeta.model].configure_attributes
|
||||||
continue if !_.contains(['input', 'select', 'integer', 'boolean', 'tree_select'], row.tag)
|
continue if !_.contains(['input', 'select', 'integer', 'boolean', 'tree_select', 'date', 'datetime'], row.tag)
|
||||||
continue if groupKey is 'ticket' && _.contains(['number', 'organization_id', 'title'], row.name)
|
continue if _.contains(['created_at', 'updated_at'], row.name)
|
||||||
|
continue if groupKey is 'ticket' && _.contains(['number', 'organization_id', 'title', 'escalation_at', 'first_response_escalation_at', 'update_escalation_at', 'close_escalation_at', 'last_contact_at', 'last_contact_agent_at', 'last_contact_customer_at', 'first_response_at', 'close_at'], row.name)
|
||||||
|
|
||||||
# ignore passwords and relations
|
# ignore passwords and relations
|
||||||
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids' && row.searchable isnt false
|
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids' && row.searchable isnt false
|
||||||
|
@ -126,11 +128,12 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
|
||||||
super(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
|
super(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
|
||||||
|
|
||||||
@buildValueConfigMultiple: (config, meta) ->
|
@buildValueConfigMultiple: (config, meta) ->
|
||||||
if _.contains(['add_option', 'remove_option', 'set_fixed_to'], meta.operator)
|
if _.contains(['add_option', 'remove_option', 'set_fixed_to', 'select'], meta.operator)
|
||||||
config.multiple = true
|
config.multiple = true
|
||||||
|
config.nulloption = true
|
||||||
else
|
else
|
||||||
config.multiple = false
|
config.multiple = false
|
||||||
config.nulloption = false
|
config.nulloption = false
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@HasPreCondition: ->
|
@HasPreCondition: ->
|
||||||
|
|
|
@ -244,7 +244,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
|
||||||
params: params
|
params: params
|
||||||
)
|
)
|
||||||
configureAttributes = [
|
configureAttributes = [
|
||||||
{ name: 'data_option::diff', display: 'Default time Diff (minutes)', tag: 'integer', null: false, default: 24 },
|
{ name: 'data_option::diff', display: 'Default time Diff (minutes)', tag: 'integer', null: true },
|
||||||
]
|
]
|
||||||
datetimeDiff = new App.ControllerForm(
|
datetimeDiff = new App.ControllerForm(
|
||||||
model:
|
model:
|
||||||
|
@ -258,7 +258,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
|
||||||
|
|
||||||
@date: (item, localParams, params) ->
|
@date: (item, localParams, params) ->
|
||||||
configureAttributes = [
|
configureAttributes = [
|
||||||
{ name: 'data_option::diff', display: 'Default time Diff (hours)', tag: 'integer', null: false, default: 24 },
|
{ name: 'data_option::diff', display: 'Default time Diff (hours)', tag: 'integer', null: true },
|
||||||
]
|
]
|
||||||
dateDiff = new App.ControllerForm(
|
dateDiff = new App.ControllerForm(
|
||||||
model:
|
model:
|
||||||
|
|
|
@ -6,7 +6,7 @@ class App.UiElement.richtext
|
||||||
attribute.value = attribute.value.text
|
attribute.value = attribute.value.text
|
||||||
|
|
||||||
item = $( App.view('generic/richtext')(attribute: attribute, toolButtons: @toolButtons) )
|
item = $( App.view('generic/richtext')(attribute: attribute, toolButtons: @toolButtons) )
|
||||||
@contenteditable = item.find('[contenteditable]').ce(
|
item.find('[contenteditable]').ce(
|
||||||
mode: attribute.type
|
mode: attribute.type
|
||||||
maxlength: attribute.maxlength
|
maxlength: attribute.maxlength
|
||||||
buttons: attribute.buttons
|
buttons: attribute.buttons
|
||||||
|
@ -21,12 +21,14 @@ class App.UiElement.richtext
|
||||||
new App[plugin.controller](params)
|
new App[plugin.controller](params)
|
||||||
|
|
||||||
if attribute.upload
|
if attribute.upload
|
||||||
@attachments = []
|
attachments = []
|
||||||
item.append( $( App.view('generic/attachment')(attribute: attribute) ) )
|
item.append( $( App.view('generic/attachment')(attribute: attribute) ) )
|
||||||
|
|
||||||
renderFile = (file) =>
|
renderFile = (file) ->
|
||||||
item.find('.attachments').append(App.view('generic/attachment_item')(file))
|
item.find('.attachments').append(App.view('generic/attachment_item')(file))
|
||||||
@attachments.push file
|
attachments.push file
|
||||||
|
if form.richTextUploadRenderCallback
|
||||||
|
form.richTextUploadRenderCallback(attribute, attachments)
|
||||||
|
|
||||||
if params && params.attachments
|
if params && params.attachments
|
||||||
for file in params.attachments
|
for file in params.attachments
|
||||||
|
@ -46,14 +48,16 @@ class App.UiElement.richtext
|
||||||
, form.form_id)
|
, form.form_id)
|
||||||
|
|
||||||
# remove items
|
# remove items
|
||||||
item.find('.attachments').on('click', '.js-delete', (e) =>
|
item.find('.attachments').on('click', '.js-delete', (e) ->
|
||||||
id = $(e.currentTarget).data('id')
|
id = $(e.currentTarget).data('id')
|
||||||
@attachments = _.filter(
|
attachments = _.filter(
|
||||||
@attachments,
|
attachments,
|
||||||
(item) ->
|
(item) ->
|
||||||
return if item.id.toString() is id.toString()
|
return if item.id.toString() is id.toString()
|
||||||
item
|
item
|
||||||
)
|
)
|
||||||
|
if form.richTextUploadDeleteCallback
|
||||||
|
form.richTextUploadDeleteCallback(attribute, attachments)
|
||||||
|
|
||||||
form_id = item.closest('form').find('[name=form_id]').val()
|
form_id = item.closest('form').find('[name=form_id]').val()
|
||||||
|
|
||||||
|
@ -71,67 +75,35 @@ class App.UiElement.richtext
|
||||||
element.empty()
|
element.empty()
|
||||||
)
|
)
|
||||||
|
|
||||||
@progressBar = item.find('.attachmentUpload-progressBar')
|
App.Delay.set( ->
|
||||||
@progressText = item.find('.js-percentage')
|
uploader = new App.Html5Upload(
|
||||||
@attachmentPlaceholder = item.find('.attachmentPlaceholder')
|
uploadUrl: "#{App.Config.get('api_path')}/attachments"
|
||||||
@attachmentUpload = item.find('.attachmentUpload')
|
dropContainer: item.closest('form')
|
||||||
@attachmentsHolder = item.find('.attachments')
|
cancelContainer: item.find('.js-cancel')
|
||||||
@cancelContainer = item.find('.js-cancel')
|
inputField: item.find('input')
|
||||||
|
data:
|
||||||
|
form_id: item.closest('form').find('[name=form_id]').val()
|
||||||
|
|
||||||
u = => html5Upload.initialize(
|
onFileStartCallback: ->
|
||||||
uploadUrl: "#{App.Config.get('api_path')}/attachments"
|
item.find('[contenteditable]').trigger('fileUploadStart')
|
||||||
dropContainer: item.closest('form').get(0)
|
|
||||||
cancelContainer: @cancelContainer
|
|
||||||
inputField: item.find('input').get(0)
|
|
||||||
maxSimultaneousUploads: 1,
|
|
||||||
key: 'File'
|
|
||||||
data:
|
|
||||||
form_id: item.closest('form').find('[name=form_id]').val()
|
|
||||||
onFileAdded: (file) =>
|
|
||||||
|
|
||||||
file.on(
|
onFileCompletedCallback: (response) ->
|
||||||
onStart: =>
|
renderFile(response.data)
|
||||||
@attachmentPlaceholder.addClass('hide')
|
item.find('input').val('')
|
||||||
@attachmentUpload.removeClass('hide')
|
item.find('[contenteditable]').trigger('fileUploadStop', ['completed'])
|
||||||
@cancelContainer.removeClass('hide')
|
|
||||||
item.find('[contenteditable]').trigger('fileUploadStart')
|
|
||||||
App.Log.debug 'UiElement.richtext', 'upload start'
|
|
||||||
|
|
||||||
onAborted: =>
|
onFileAbortedCallback: ->
|
||||||
@attachmentPlaceholder.removeClass('hide')
|
item.find('input').val('')
|
||||||
@attachmentUpload.addClass('hide')
|
item.find('[contenteditable]').trigger('fileUploadStop', ['aborted'])
|
||||||
item.find('input').val('')
|
|
||||||
item.find('[contenteditable]').trigger('fileUploadStop', ['aborted'])
|
|
||||||
|
|
||||||
# Called after received response from the server
|
attachmentPlaceholder: item.find('.attachmentPlaceholder')
|
||||||
onCompleted: (response) =>
|
attachmentUpload: item.find('.attachmentUpload')
|
||||||
response = JSON.parse(response)
|
progressBar: item.find('.attachmentUpload-progressBar')
|
||||||
|
progressText: item.find('.js-percentage')
|
||||||
|
)
|
||||||
|
|
||||||
@attachmentPlaceholder.removeClass('hide')
|
uploader.render()
|
||||||
@attachmentUpload.addClass('hide')
|
, 100, undefined, 'form_upload')
|
||||||
|
|
||||||
# reset progress bar
|
|
||||||
@progressBar.width(parseInt(0) + '%')
|
|
||||||
@progressText.text('')
|
|
||||||
|
|
||||||
renderFile(response.data)
|
|
||||||
item.find('input').val('')
|
|
||||||
item.find('[contenteditable]').trigger('fileUploadStop', ['completed'])
|
|
||||||
App.Log.debug 'UiElement.richtext', 'upload complete', response.data
|
|
||||||
|
|
||||||
# Called during upload progress, first parameter
|
|
||||||
# is decimal value from 0 to 100.
|
|
||||||
onProgress: (progress, fileSize, uploadedBytes) =>
|
|
||||||
@progressBar.width(parseInt(progress) + '%')
|
|
||||||
@progressText.text(parseInt(progress))
|
|
||||||
# hide cancel on 90%
|
|
||||||
if parseInt(progress) >= 90
|
|
||||||
@cancelContainer.addClass('hide')
|
|
||||||
App.Log.debug 'UiElement.richtext', 'uploadProgress ', parseInt(progress)
|
|
||||||
|
|
||||||
)
|
|
||||||
)
|
|
||||||
App.Delay.set(u, 100, undefined, 'form_upload')
|
|
||||||
|
|
||||||
item
|
item
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@ class App.UiElement.sla_times
|
||||||
item = $( App.view('generic/sla_times')(
|
item = $( App.view('generic/sla_times')(
|
||||||
attribute: attribute
|
attribute: attribute
|
||||||
first_response_time: params.first_response_time
|
first_response_time: params.first_response_time
|
||||||
|
response_time: params.response_time
|
||||||
update_time: params.update_time
|
update_time: params.update_time
|
||||||
solution_time: params.solution_time
|
solution_time: params.solution_time
|
||||||
first_response_time_in_text: @toText(params.first_response_time)
|
first_response_time_in_text: @toText(params.first_response_time)
|
||||||
|
response_time_in_text: @toText(params.response_time)
|
||||||
update_time_in_text: @toText(params.update_time)
|
update_time_in_text: @toText(params.update_time)
|
||||||
solution_time_in_text: @toText(params.solution_time)
|
solution_time_in_text: @toText(params.solution_time)
|
||||||
) )
|
) )
|
||||||
|
@ -26,12 +28,16 @@ class App.UiElement.sla_times
|
||||||
row = element.closest('tr')
|
row = element.closest('tr')
|
||||||
if element.prop('checked')
|
if element.prop('checked')
|
||||||
row.addClass('is-active')
|
row.addClass('is-active')
|
||||||
|
|
||||||
|
if row.has('.js-updateTypeSelector').length > 0 && row.has('.js-updateTypeSelector:checked').length == 0
|
||||||
|
row.find('.js-updateTypeSelector:first').prop('checked', true)
|
||||||
else
|
else
|
||||||
row.removeClass('is-active')
|
row.removeClass('is-active')
|
||||||
|
|
||||||
# reset data item
|
# reset data item
|
||||||
row.find('.js-timeConvertFrom').val('')
|
row.find('.js-timeConvertFrom').val('')
|
||||||
row.find('.js-timeConvertTo').val('')
|
row.find('.js-timeConvertTo').val('')
|
||||||
|
row.find('.js-updateTypeSelector').attr('checked', false)
|
||||||
row.find('.help-inline').empty()
|
row.find('.help-inline').empty()
|
||||||
row.removeClass('has-error')
|
row.removeClass('has-error')
|
||||||
)
|
)
|
||||||
|
@ -42,12 +48,16 @@ class App.UiElement.sla_times
|
||||||
inText = element.val()
|
inText = element.val()
|
||||||
|
|
||||||
row = element.closest('tr')
|
row = element.closest('tr')
|
||||||
row.find('.js-activateRow').prop('checked', true)
|
|
||||||
|
row
|
||||||
|
.find('.js-activateRow')
|
||||||
|
.prop('checked', true)
|
||||||
|
.trigger('change')
|
||||||
|
|
||||||
row.addClass('is-active')
|
row.addClass('is-active')
|
||||||
|
|
||||||
element
|
row
|
||||||
.closest('td')
|
.find("[name='#{element.data('name')}']")
|
||||||
.find('.js-timeConvertTo')
|
|
||||||
.val(@toMinutes(inText) || '')
|
.val(@toMinutes(inText) || '')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,9 +66,19 @@ class App.UiElement.sla_times
|
||||||
$(e.currentTarget).closest('tr').find('.checkbox-replacement').click()
|
$(e.currentTarget).closest('tr').find('.checkbox-replacement').click()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# toggle update type on clicking around the element
|
||||||
|
item.find('.js-forward-radio').bind('click', (e) ->
|
||||||
|
elem = $(e.currentTarget).closest('p').find('.js-updateTypeSelector')
|
||||||
|
|
||||||
|
elem.prop('checked', true)
|
||||||
|
elem.trigger('change')
|
||||||
|
)
|
||||||
|
|
||||||
# focus time input on clicking surrounding cell
|
# focus time input on clicking surrounding cell
|
||||||
item.find('.js-focus-input').bind('click', (e) ->
|
item.find('.js-focus-input').bind('click', (e) ->
|
||||||
$(e.currentTarget).find('.form-control').focus()
|
$(e.currentTarget)
|
||||||
|
.find('.form-control:visible')
|
||||||
|
.focus()
|
||||||
)
|
)
|
||||||
|
|
||||||
# show placeholder instead of 00:00
|
# show placeholder instead of 00:00
|
||||||
|
@ -67,15 +87,36 @@ class App.UiElement.sla_times
|
||||||
$(e.currentTarget).val('')
|
$(e.currentTarget).val('')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# switch update/response times when type is selected accordingly
|
||||||
|
item.find('.js-updateTypeSelector').bind('change', (e) ->
|
||||||
|
element = $(e.target)
|
||||||
|
row = element.closest('tr')
|
||||||
|
row.find('.js-activateRow').prop('checked', true)
|
||||||
|
row.addClass('is-active')
|
||||||
|
|
||||||
|
row
|
||||||
|
.find('.js-timeConvertFrom')
|
||||||
|
.addClass('hidden')
|
||||||
|
.val('')
|
||||||
|
|
||||||
|
row
|
||||||
|
.find('.js-timeConvertTo')
|
||||||
|
.val('')
|
||||||
|
|
||||||
|
row
|
||||||
|
.find("[data-name='#{element.val()}_time']")
|
||||||
|
.removeClass('hidden')
|
||||||
|
)
|
||||||
|
|
||||||
# set initial active/inactive rows
|
# set initial active/inactive rows
|
||||||
item.find('.js-timeConvertFrom').each(->
|
item.find('.js-timeConvertFrom').each(->
|
||||||
row = $(@).closest('tr')
|
row = $(@).closest('tr')
|
||||||
checkbox = row.find('.js-activateRow')
|
checkbox = row.find('.js-activateRow')
|
||||||
if $(@).val()
|
|
||||||
checkbox.prop('checked', true)
|
return if !$(@).val()
|
||||||
row.addClass('is-active')
|
|
||||||
else
|
checkbox.prop('checked', true)
|
||||||
checkbox.prop('checked', false)
|
row.addClass('is-active')
|
||||||
)
|
)
|
||||||
|
|
||||||
item
|
item
|
||||||
|
|
|
@ -4,16 +4,17 @@ class App.UiElement.textarea
|
||||||
fileUploaderId = 'file-uploader-' + new Date().getTime() + '-' + Math.floor( Math.random() * 99999 )
|
fileUploaderId = 'file-uploader-' + new Date().getTime() + '-' + Math.floor( Math.random() * 99999 )
|
||||||
item = $( App.view('generic/textarea')( attribute: attribute ) + '<div class="file-uploader ' + attribute.class + '" id="' + fileUploaderId + '"></div>' )
|
item = $( App.view('generic/textarea')( attribute: attribute ) + '<div class="file-uploader ' + attribute.class + '" id="' + fileUploaderId + '"></div>' )
|
||||||
|
|
||||||
a = ->
|
if attribute.expanding isnt false
|
||||||
visible = $( item[0] ).is(':visible')
|
a = ->
|
||||||
if visible && !$( item[0] ).expanding('active')
|
|
||||||
$( item[0] ).expanding()
|
|
||||||
$( item[0] ).on('focus', ->
|
|
||||||
visible = $( item[0] ).is(':visible')
|
visible = $( item[0] ).is(':visible')
|
||||||
if visible && !$( item[0] ).expanding('active')
|
if visible && !$( item[0] ).expanding('active')
|
||||||
$( item[0] ).expanding().focus()
|
$( item[0] ).expanding()
|
||||||
)
|
$( item[0] ).on('focus', ->
|
||||||
App.Delay.set(a, 80)
|
visible = $( item[0] ).is(':visible')
|
||||||
|
if visible && !$( item[0] ).expanding('active')
|
||||||
|
$( item[0] ).expanding().focus()
|
||||||
|
)
|
||||||
|
App.Delay.set(a, 80)
|
||||||
|
|
||||||
if attribute.upload
|
if attribute.upload
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ class App.TicketCreate extends App.Controller
|
||||||
events:
|
events:
|
||||||
'click .type-tabs .tab': 'changeFormType'
|
'click .type-tabs .tab': 'changeFormType'
|
||||||
'submit form': 'submit'
|
'submit form': 'submit'
|
||||||
'click .js-cancel': 'cancel'
|
'click .form-controls .js-cancel': 'cancel'
|
||||||
'click .js-active-toggle': 'toggleButton'
|
'click .js-active-toggle': 'toggleButton'
|
||||||
|
|
||||||
types: {
|
types: {
|
||||||
|
@ -48,12 +48,15 @@ class App.TicketCreate extends App.Controller
|
||||||
if @ticket_id && @article_id
|
if @ticket_id && @article_id
|
||||||
@split = "/#{@ticket_id}/#{@article_id}"
|
@split = "/#{@ticket_id}/#{@article_id}"
|
||||||
|
|
||||||
load = (data) =>
|
@ajax(
|
||||||
App.Collection.loadAssets(data.assets)
|
type: 'GET'
|
||||||
@formMeta = data.form_meta
|
url: "#{@apiPath}/ticket_create"
|
||||||
@buildScreen(params)
|
processData: true
|
||||||
@bindId = App.TicketCreateCollection.bind(load, false)
|
success: (data, status, xhr) =>
|
||||||
App.TicketCreateCollection.fetch()
|
App.Collection.loadAssets(data.assets)
|
||||||
|
@formMeta = data.form_meta
|
||||||
|
@buildScreen(params)
|
||||||
|
)
|
||||||
|
|
||||||
# rerender view, e. g. on langauge change
|
# rerender view, e. g. on langauge change
|
||||||
@controllerBind('ui:rerender', =>
|
@controllerBind('ui:rerender', =>
|
||||||
|
@ -69,9 +72,6 @@ class App.TicketCreate extends App.Controller
|
||||||
@sidebarWidget.render(@params())
|
@sidebarWidget.render(@params())
|
||||||
)
|
)
|
||||||
|
|
||||||
release: =>
|
|
||||||
App.TicketCreateCollection.unbindById(@bindId)
|
|
||||||
|
|
||||||
currentChannel: =>
|
currentChannel: =>
|
||||||
if !type
|
if !type
|
||||||
type = @$('.type-tabs .tab.active').data('type')
|
type = @$('.type-tabs .tab.active').data('type')
|
||||||
|
@ -184,8 +184,11 @@ class App.TicketCreate extends App.Controller
|
||||||
@controllerUnbind('ticket_create_rerender', (template) => @renderQueue(template))
|
@controllerUnbind('ticket_create_rerender', (template) => @renderQueue(template))
|
||||||
|
|
||||||
changed: =>
|
changed: =>
|
||||||
|
return true if @hasAttachments()
|
||||||
|
|
||||||
formCurrent = @formParam( @$('.ticket-create') )
|
formCurrent = @formParam( @$('.ticket-create') )
|
||||||
diff = difference(@formDefault, formCurrent)
|
diff = difference(@formDefault, formCurrent)
|
||||||
|
|
||||||
return false if !diff || _.isEmpty(diff)
|
return false if !diff || _.isEmpty(diff)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
@ -279,8 +282,15 @@ class App.TicketCreate extends App.Controller
|
||||||
return if !@formMeta
|
return if !@formMeta
|
||||||
App.QueueManager.run(@queueKey)
|
App.QueueManager.run(@queueKey)
|
||||||
|
|
||||||
|
updateTaskManagerAttachments: (attribute, attachments) =>
|
||||||
|
taskData = App.TaskManager.get(@taskKey)
|
||||||
|
return if _.isEmpty(taskData)
|
||||||
|
|
||||||
|
taskData.attachments = attachments
|
||||||
|
App.TaskManager.update(@taskKey, taskData)
|
||||||
|
|
||||||
render: (template = {}) ->
|
render: (template = {}) ->
|
||||||
return if !@formMeta
|
|
||||||
# get params
|
# get params
|
||||||
params = @prefilledParams || {}
|
params = @prefilledParams || {}
|
||||||
if template && !_.isEmpty(template.options)
|
if template && !_.isEmpty(template.options)
|
||||||
|
@ -322,17 +332,16 @@ class App.TicketCreate extends App.Controller
|
||||||
handlers = @Config.get('TicketCreateFormHandler')
|
handlers = @Config.get('TicketCreateFormHandler')
|
||||||
|
|
||||||
@controllerFormCreateMiddle = new App.ControllerForm(
|
@controllerFormCreateMiddle = new App.ControllerForm(
|
||||||
el: @$('.ticket-form-middle')
|
el: @$('.ticket-form-middle')
|
||||||
form_id: @formId
|
form_id: @formId
|
||||||
model: App.Ticket
|
model: App.Ticket
|
||||||
screen: 'create_middle'
|
screen: 'create_middle'
|
||||||
handlersConfig: handlers
|
handlersConfig: handlers
|
||||||
filter: @formMeta.filter
|
formMeta: @formMeta
|
||||||
formMeta: @formMeta
|
params: params
|
||||||
params: params
|
noFieldset: true
|
||||||
noFieldset: true
|
taskKey: @taskKey
|
||||||
taskKey: @taskKey
|
rejectNonExistentValues: true
|
||||||
rejectNonExistentValues: true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# tunnel events to make sure core workflow does know
|
# tunnel events to make sure core workflow does know
|
||||||
|
@ -356,8 +365,6 @@ class App.TicketCreate extends App.Controller
|
||||||
events:
|
events:
|
||||||
'change [name=customer_id]': @localUserInfo
|
'change [name=customer_id]': @localUserInfo
|
||||||
handlersConfig: handlersTunnel
|
handlersConfig: handlersTunnel
|
||||||
filter: @formMeta.filter
|
|
||||||
formMeta: @formMeta
|
|
||||||
autofocus: true
|
autofocus: true
|
||||||
params: params
|
params: params
|
||||||
taskKey: @taskKey
|
taskKey: @taskKey
|
||||||
|
@ -373,6 +380,8 @@ class App.TicketCreate extends App.Controller
|
||||||
handlersConfig: handlersTunnel
|
handlersConfig: handlersTunnel
|
||||||
params: params
|
params: params
|
||||||
taskKey: @taskKey
|
taskKey: @taskKey
|
||||||
|
richTextUploadRenderCallback: @updateTaskManagerAttachments
|
||||||
|
richTextUploadDeleteCallback: @updateTaskManagerAttachments
|
||||||
)
|
)
|
||||||
@controllerFormCreateBottom = new App.ControllerForm(
|
@controllerFormCreateBottom = new App.ControllerForm(
|
||||||
el: @$('.ticket-form-bottom')
|
el: @$('.ticket-form-bottom')
|
||||||
|
@ -380,8 +389,6 @@ class App.TicketCreate extends App.Controller
|
||||||
model: App.Ticket
|
model: App.Ticket
|
||||||
screen: 'create_bottom'
|
screen: 'create_bottom'
|
||||||
handlersConfig: handlersTunnel
|
handlersConfig: handlersTunnel
|
||||||
filter: @formMeta.filter
|
|
||||||
formMeta: @formMeta
|
|
||||||
params: params
|
params: params
|
||||||
taskKey: @taskKey
|
taskKey: @taskKey
|
||||||
)
|
)
|
||||||
|
@ -461,6 +468,9 @@ class App.TicketCreate extends App.Controller
|
||||||
params: =>
|
params: =>
|
||||||
params = @formParam(@$('.main form'))
|
params = @formParam(@$('.main form'))
|
||||||
|
|
||||||
|
hasAttachments: =>
|
||||||
|
@$('.richtext .attachments .attachment').length > 0
|
||||||
|
|
||||||
submit: (e) =>
|
submit: (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
|
@ -563,7 +573,7 @@ class App.TicketCreate extends App.Controller
|
||||||
# save ticket, create article
|
# save ticket, create article
|
||||||
# check attachment
|
# check attachment
|
||||||
if article['body']
|
if article['body']
|
||||||
if @$('.richtext .attachments .attachment').length < 1
|
if !@hasAttachments()
|
||||||
matchingWord = App.Utils.checkAttachmentReference(article['body'])
|
matchingWord = App.Utils.checkAttachmentReference(article['body'])
|
||||||
if matchingWord
|
if matchingWord
|
||||||
if !confirm(App.i18n.translateContent('You use %s in text but no attachment is attached. Do you want to continue?', matchingWord))
|
if !confirm(App.i18n.translateContent('You use %s in text but no attachment is attached. Do you want to continue?', matchingWord))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class TicketCreateFormHanderSignature
|
class TicketCreateFormHandlerSignature
|
||||||
|
|
||||||
@run: (params, attribute, attributes, classname, form, ui) ->
|
@run: (params, attribute, attributes, classname, form, ui) ->
|
||||||
return if !attribute
|
return if !attribute
|
||||||
|
@ -19,10 +19,7 @@ class TicketCreateFormHanderSignature
|
||||||
if App.Utils.signatureCheck(currentBody.html() || '', signatureFinished)
|
if App.Utils.signatureCheck(currentBody.html() || '', signatureFinished)
|
||||||
|
|
||||||
# if signature has changed, in case remove old signature
|
# if signature has changed, in case remove old signature
|
||||||
signature_id = ui.el.closest('.content').find('[data-signature=true]').data('signature-id')
|
ui.el.closest('.content').find('[data-signature="true"]').remove()
|
||||||
if signature_id && signature_id.toString() isnt signature.id.toString()
|
|
||||||
|
|
||||||
ui.el.closest('.content').find('[data-signature="true"]').remove()
|
|
||||||
|
|
||||||
if !App.Utils.htmlLastLineEmpty(currentBody)
|
if !App.Utils.htmlLastLineEmpty(currentBody)
|
||||||
currentBody.append('<br><br>')
|
currentBody.append('<br><br>')
|
||||||
|
@ -35,4 +32,4 @@ class TicketCreateFormHanderSignature
|
||||||
else
|
else
|
||||||
ui.el.closest('.content').find('[data-name="body"]').find('[data-signature=true]').remove()
|
ui.el.closest('.content').find('[data-name="body"]').find('[data-signature=true]').remove()
|
||||||
|
|
||||||
App.Config.set('200-ticketFormSignature', TicketCreateFormHanderSignature, 'TicketCreateFormHandler')
|
App.Config.set('200-ticketFormSignature', TicketCreateFormHandlerSignature, 'TicketCreateFormHandler')
|
|
@ -1,6 +1,6 @@
|
||||||
class CoreWorkflow extends App.ControllerSubContent
|
class CoreWorkflow extends App.ControllerSubContent
|
||||||
requiredPermission: 'admin.core_workflow'
|
requiredPermission: 'admin.core_workflow'
|
||||||
header: 'Core Workflow'
|
header: 'Core Workflows'
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
|
||||||
|
@ -54,4 +54,4 @@ class CoreWorkflow extends App.ControllerSubContent
|
||||||
}
|
}
|
||||||
return mapping[screen] || screen
|
return mapping[screen] || screen
|
||||||
|
|
||||||
App.Config.set('CoreWorkflowObject', { prio: 1750, parent: '#system', name: 'Core Workflow', target: '#system/core_workflow', controller: CoreWorkflow, permission: ['admin.core_workflow'] }, 'NavBarAdmin')
|
App.Config.set('CoreWorkflowObject', { prio: 1750, parent: '#system', name: 'Core Workflows', target: '#system/core_workflow', controller: CoreWorkflow, permission: ['admin.core_workflow'] }, 'NavBarAdmin')
|
||||||
|
|
|
@ -13,13 +13,7 @@ class CustomerTicketCreate extends App.ControllerAppContent
|
||||||
@form_id = App.ControllerForm.formId()
|
@form_id = App.ControllerForm.formId()
|
||||||
|
|
||||||
@navupdate '#customer_ticket_new'
|
@navupdate '#customer_ticket_new'
|
||||||
|
@render()
|
||||||
load = (data) =>
|
|
||||||
App.Collection.loadAssets(data.assets)
|
|
||||||
@formMeta = data.form_meta
|
|
||||||
@render()
|
|
||||||
@bindId = App.TicketCreateCollection.bind(load, false)
|
|
||||||
App.TicketCreateCollection.fetch()
|
|
||||||
|
|
||||||
render: (template = {}) ->
|
render: (template = {}) ->
|
||||||
if !@Config.get('customer_ticket_create')
|
if !@Config.get('customer_ticket_create')
|
||||||
|
@ -43,8 +37,6 @@ class CustomerTicketCreate extends App.ControllerAppContent
|
||||||
form_id: @form_id
|
form_id: @form_id
|
||||||
model: App.Ticket
|
model: App.Ticket
|
||||||
screen: 'create_middle'
|
screen: 'create_middle'
|
||||||
filter: @formMeta.filter
|
|
||||||
formMeta: @formMeta
|
|
||||||
params: defaults
|
params: defaults
|
||||||
noFieldset: true
|
noFieldset: true
|
||||||
handlersConfig: handlers
|
handlersConfig: handlers
|
||||||
|
@ -70,8 +62,6 @@ class CustomerTicketCreate extends App.ControllerAppContent
|
||||||
model: App.Ticket
|
model: App.Ticket
|
||||||
screen: 'create_top'
|
screen: 'create_top'
|
||||||
handlersConfig: handlersTunnel
|
handlersConfig: handlersTunnel
|
||||||
filter: @formMeta.filter
|
|
||||||
formMeta: @formMeta
|
|
||||||
autofocus: true
|
autofocus: true
|
||||||
params: defaults
|
params: defaults
|
||||||
)
|
)
|
||||||
|
@ -83,8 +73,6 @@ class CustomerTicketCreate extends App.ControllerAppContent
|
||||||
events:
|
events:
|
||||||
'fileUploadStart .richtext': => @submitDisable()
|
'fileUploadStart .richtext': => @submitDisable()
|
||||||
'fileUploadStop .richtext': => @submitEnable()
|
'fileUploadStop .richtext': => @submitEnable()
|
||||||
filter: @formMeta.filter
|
|
||||||
formMeta: @formMeta
|
|
||||||
params: defaults
|
params: defaults
|
||||||
handlersConfig: handlersTunnel
|
handlersConfig: handlersTunnel
|
||||||
)
|
)
|
||||||
|
@ -95,8 +83,6 @@ class CustomerTicketCreate extends App.ControllerAppContent
|
||||||
model: App.Ticket
|
model: App.Ticket
|
||||||
screen: 'create_bottom'
|
screen: 'create_bottom'
|
||||||
handlersConfig: handlersTunnel
|
handlersConfig: handlersTunnel
|
||||||
filter: @formMeta.filter
|
|
||||||
formMeta: @formMeta
|
|
||||||
params: defaults
|
params: defaults
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -91,20 +91,7 @@ class GettingStartedChannelEmail extends App.ControllerWizardFullScreen
|
||||||
ui.hide('options::folder')
|
ui.hide('options::folder')
|
||||||
ui.hide('options::keep_on_server')
|
ui.hide('options::keep_on_server')
|
||||||
|
|
||||||
handlePort = (params, attribute, attributes, classname, form, ui) ->
|
form = new App.ControllerForm(
|
||||||
return if !params
|
|
||||||
return if !params.options
|
|
||||||
currentPort = @$('.base-inbound-settings [name="options::port"]').val()
|
|
||||||
if params.options.ssl is true
|
|
||||||
if !currentPort
|
|
||||||
@$('.base-inbound-settings [name="options::port"]').val('993')
|
|
||||||
return
|
|
||||||
if params.options.ssl is false
|
|
||||||
if !currentPort || currentPort is '993'
|
|
||||||
@$('.base-inbound-settings [name="options::port"]').val('143')
|
|
||||||
return
|
|
||||||
|
|
||||||
new App.ControllerForm(
|
|
||||||
el: @$('.base-inbound-settings')
|
el: @$('.base-inbound-settings')
|
||||||
model:
|
model:
|
||||||
configure_attributes: configureAttributesInbound
|
configure_attributes: configureAttributesInbound
|
||||||
|
@ -112,10 +99,16 @@ class GettingStartedChannelEmail extends App.ControllerWizardFullScreen
|
||||||
params: @account.inbound
|
params: @account.inbound
|
||||||
handlers: [
|
handlers: [
|
||||||
showHideFolder,
|
showHideFolder,
|
||||||
handlePort,
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
form.el.find("select[name='options::ssl']").off('change').on('change', (e) ->
|
||||||
|
if $(e.target).val() is 'true'
|
||||||
|
form.el.find("[name='options::port']").val('993')
|
||||||
|
else
|
||||||
|
form.el.find("[name='options::port']").val('143')
|
||||||
|
)
|
||||||
|
|
||||||
toggleOutboundAdapter: =>
|
toggleOutboundAdapter: =>
|
||||||
|
|
||||||
# fill user / password based on intro info
|
# fill user / password based on intro info
|
||||||
|
|
|
@ -165,7 +165,7 @@ class ImportFreshdesk extends App.ControllerWizardFullScreen
|
||||||
@$('.js-error').addClass('hide')
|
@$('.js-error').addClass('hide')
|
||||||
|
|
||||||
if !_.isEmpty(data.finished_at) && _.isEmpty(data.result['error'])
|
if !_.isEmpty(data.finished_at) && _.isEmpty(data.result['error'])
|
||||||
window.location.reload()
|
@redirectToLogin()
|
||||||
return
|
return
|
||||||
|
|
||||||
if !_.isEmpty(data.result)
|
if !_.isEmpty(data.result)
|
||||||
|
|
|
@ -163,7 +163,7 @@ class ImportZendesk extends App.ControllerWizardFullScreen
|
||||||
@$('.js-error').addClass('hide')
|
@$('.js-error').addClass('hide')
|
||||||
|
|
||||||
if !_.isEmpty(data.finished_at) && _.isEmpty(data.result['error'])
|
if !_.isEmpty(data.finished_at) && _.isEmpty(data.result['error'])
|
||||||
window.location.reload()
|
@redirectToLogin()
|
||||||
return
|
return
|
||||||
|
|
||||||
if !_.isEmpty(data.result)
|
if !_.isEmpty(data.result)
|
||||||
|
|
|
@ -27,11 +27,13 @@ class App.KnowledgeBasePublicMenuManager extends App.Controller
|
||||||
{
|
{
|
||||||
headline: 'Header menu',
|
headline: 'Header menu',
|
||||||
identifier: 'header',
|
identifier: 'header',
|
||||||
color: kb.color_header
|
color: kb.color_header,
|
||||||
|
color_link: kb.color_header_link
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headline: 'Footer menu',
|
headline: 'Footer menu',
|
||||||
identifier: 'footer'
|
identifier: 'footer',
|
||||||
|
color_link: 'hsl(207,12%,50%)'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -247,8 +247,6 @@ class Edit extends App.ControllerGenericEdit
|
||||||
#if attribute.name is 'data_type'
|
#if attribute.name is 'data_type'
|
||||||
# attribute.disabled = true
|
# attribute.disabled = true
|
||||||
|
|
||||||
console.log('configure_attributes', configure_attributes)
|
|
||||||
|
|
||||||
@controller = new App.ControllerForm(
|
@controller = new App.ControllerForm(
|
||||||
model:
|
model:
|
||||||
configure_attributes: configure_attributes
|
configure_attributes: configure_attributes
|
||||||
|
|
|
@ -153,7 +153,7 @@ class Object extends App.ControllerObserver
|
||||||
elLocal.find('.js-userList').html(members)
|
elLocal.find('.js-userList').html(members)
|
||||||
)
|
)
|
||||||
|
|
||||||
if @organization.member_ids.length < @memberLimit
|
if @organization.member_ids.length <= @memberLimit
|
||||||
@el.find('.js-showMoreMembers').parent().addClass('hidden')
|
@el.find('.js-showMoreMembers').parent().addClass('hidden')
|
||||||
else
|
else
|
||||||
@el.find('.js-showMoreMembers').parent().removeClass('hidden')
|
@el.find('.js-showMoreMembers').parent().removeClass('hidden')
|
||||||
|
@ -202,7 +202,7 @@ class Object extends App.ControllerObserver
|
||||||
value = $(e.target).html()
|
value = $(e.target).html()
|
||||||
org = App.Organization.find(@object_id)
|
org = App.Organization.find(@object_id)
|
||||||
if org[name] isnt value
|
if org[name] isnt value
|
||||||
@lastAttributres[name] = value
|
@lastAttributes[name] = value
|
||||||
data = {}
|
data = {}
|
||||||
data[name] = value
|
data[name] = value
|
||||||
org.updateAttributes(data)
|
org.updateAttributes(data)
|
||||||
|
|
|
@ -171,6 +171,13 @@ class Graph extends App.Controller
|
||||||
backends: @params.backendSelected
|
backends: @params.backendSelected
|
||||||
)
|
)
|
||||||
processData: true
|
processData: true
|
||||||
|
error: (xhr) =>
|
||||||
|
return if !_.include([401, 403, 404, 422, 502], xhr.status)
|
||||||
|
|
||||||
|
@bodyModal = new App.ControllerTechnicalErrorModal(
|
||||||
|
head: 'Cannot generate report'
|
||||||
|
contentCode: xhr.responseJSON.error
|
||||||
|
)
|
||||||
success: (data) =>
|
success: (data) =>
|
||||||
@update(data)
|
@update(data)
|
||||||
@delay(@render, interval, 'report-update', 'page')
|
@delay(@render, interval, 'report-update', 'page')
|
||||||
|
|
|
@ -56,13 +56,9 @@ class App.Search extends App.Controller
|
||||||
@navupdate(url: '#search', type: 'menu')
|
@navupdate(url: '#search', type: 'menu')
|
||||||
return if _.isEmpty(params.query)
|
return if _.isEmpty(params.query)
|
||||||
|
|
||||||
@$('.js-search').val(params.query).trigger('change')
|
@$('.js-search').val(params.query).trigger('keyup')
|
||||||
return if @shown
|
|
||||||
|
|
||||||
@search(1000, true)
|
|
||||||
|
|
||||||
hide: ->
|
hide: ->
|
||||||
@shown = false
|
|
||||||
if @table
|
if @table
|
||||||
@table.hide()
|
@table.hide()
|
||||||
|
|
||||||
|
@ -108,6 +104,7 @@ class App.Search extends App.Controller
|
||||||
return
|
return
|
||||||
|
|
||||||
# on other keys, show result
|
# on other keys, show result
|
||||||
|
@navigate "#search/#{encodeURIComponent(@searchInput.val())}"
|
||||||
@search(0)
|
@search(0)
|
||||||
|
|
||||||
empty: =>
|
empty: =>
|
||||||
|
|
|
@ -26,12 +26,6 @@ class Sla extends App.ControllerSubContent
|
||||||
sortBy: 'name'
|
sortBy: 'name'
|
||||||
)
|
)
|
||||||
for sla in slas
|
for sla in slas
|
||||||
if sla.first_response_time
|
|
||||||
sla.first_response_time_in_text = @toText(sla.first_response_time)
|
|
||||||
if sla.update_time
|
|
||||||
sla.update_time_in_text = @toText(sla.update_time)
|
|
||||||
if sla.solution_time
|
|
||||||
sla.solution_time_in_text = @toText(sla.solution_time)
|
|
||||||
sla.rules = App.UiElement.ticket_selector.humanText(sla.condition)
|
sla.rules = App.UiElement.ticket_selector.humanText(sla.condition)
|
||||||
sla.calendar = App.Calendar.find(sla.calendar_id)
|
sla.calendar = App.Calendar.find(sla.calendar_id)
|
||||||
|
|
||||||
|
@ -95,21 +89,8 @@ class Sla extends App.ControllerSubContent
|
||||||
|
|
||||||
description: (e) =>
|
description: (e) =>
|
||||||
new App.ControllerGenericDescription(
|
new App.ControllerGenericDescription(
|
||||||
description: App.Calendar.description
|
description: App.Sla.description
|
||||||
container: @el.closest('.content')
|
container: @el.closest('.content')
|
||||||
)
|
)
|
||||||
|
|
||||||
toText: (m) ->
|
|
||||||
m = parseInt(m)
|
|
||||||
return if !m
|
|
||||||
minutes = m % 60
|
|
||||||
hours = Math.floor(m / 60)
|
|
||||||
|
|
||||||
if minutes < 10
|
|
||||||
minutes = "0#{minutes}"
|
|
||||||
if hours < 10
|
|
||||||
hours = "0#{hours}"
|
|
||||||
|
|
||||||
"#{hours}:#{minutes}"
|
|
||||||
|
|
||||||
App.Config.set('Sla', { prio: 2900, name: 'SLAs', parent: '#manage', target: '#manage/slas', controller: Sla, permission: ['admin.sla'] }, 'NavBarAdmin')
|
App.Config.set('Sla', { prio: 2900, name: 'SLAs', parent: '#manage', target: '#manage/slas', controller: Sla, permission: ['admin.sla'] }, 'NavBarAdmin')
|
||||||
|
|
|
@ -206,22 +206,13 @@ class App.TicketOverview extends App.Controller
|
||||||
article: article
|
article: article
|
||||||
)
|
)
|
||||||
ticket.article = article
|
ticket.article = article
|
||||||
ticket.ajax().update(
|
ticket.save(
|
||||||
ticket.attributes()
|
|
||||||
# this option will prevent callbacks and invalid data states in case of an error
|
|
||||||
failResponseNoTrigger: true
|
|
||||||
done: (r) =>
|
done: (r) =>
|
||||||
@batchCountIndex++
|
@batchCountIndex++
|
||||||
|
|
||||||
# refresh view after all tickets are proceeded
|
# refresh view after all tickets are proceeded
|
||||||
if @batchCountIndex == @batchCount
|
if @batchCountIndex == @batchCount
|
||||||
App.Event.trigger('overview:fetch')
|
App.Event.trigger('overview:fetch')
|
||||||
fail: (record, settings, details) ->
|
|
||||||
console.log('record, settings, details', record, settings, details)
|
|
||||||
App.Event.trigger('notify', {
|
|
||||||
type: 'error'
|
|
||||||
msg: App.i18n.translateContent('Bulk action stopped %s!', error)
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -234,21 +225,13 @@ class App.TicketOverview extends App.Controller
|
||||||
ticket.owner_id = id
|
ticket.owner_id = id
|
||||||
if !_.isEmpty(groupId)
|
if !_.isEmpty(groupId)
|
||||||
ticket.group_id = groupId
|
ticket.group_id = groupId
|
||||||
ticket.ajax().update(
|
ticket.save(
|
||||||
ticket.attributes()
|
|
||||||
# this option will prevent callbacks and invalid data states in case of an error
|
|
||||||
failResponseNoTrigger: true
|
|
||||||
done: (r) =>
|
done: (r) =>
|
||||||
@batchCountIndex++
|
@batchCountIndex++
|
||||||
|
|
||||||
# refresh view after all tickets are proceeded
|
# refresh view after all tickets are proceeded
|
||||||
if @batchCountIndex == @batchCount
|
if @batchCountIndex == @batchCount
|
||||||
App.Event.trigger('overview:fetch')
|
App.Event.trigger('overview:fetch')
|
||||||
fail: (record, settings, details) ->
|
|
||||||
App.Event.trigger('notify', {
|
|
||||||
type: 'error'
|
|
||||||
msg: App.i18n.translateContent('Bulk action stopped %s!', settings.error)
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -259,21 +242,13 @@ class App.TicketOverview extends App.Controller
|
||||||
#console.log "perform action #{action} with id #{id} on ", $(item).val()
|
#console.log "perform action #{action} with id #{id} on ", $(item).val()
|
||||||
ticket = App.Ticket.find($(item).val())
|
ticket = App.Ticket.find($(item).val())
|
||||||
ticket.group_id = id
|
ticket.group_id = id
|
||||||
ticket.ajax().update(
|
ticket.save(
|
||||||
ticket.attributes()
|
|
||||||
# this option will prevent callbacks and invalid data states in case of an error
|
|
||||||
failResponseNoTrigger: true
|
|
||||||
done: (r) =>
|
done: (r) =>
|
||||||
@batchCountIndex++
|
@batchCountIndex++
|
||||||
|
|
||||||
# refresh view after all tickets are proceeded
|
# refresh view after all tickets are proceeded
|
||||||
if @batchCountIndex == @batchCount
|
if @batchCountIndex == @batchCount
|
||||||
App.Event.trigger('overview:fetch')
|
App.Event.trigger('overview:fetch')
|
||||||
fail: (record, settings, details) ->
|
|
||||||
App.Event.trigger('notify', {
|
|
||||||
type: 'error'
|
|
||||||
msg: App.i18n.translateContent('Bulk action stopped %s!', error)
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1314,7 +1289,7 @@ class Table extends App.Controller
|
||||||
return if ticketListShow[0] || @permissionCheck('ticket.agent')
|
return if ticketListShow[0] || @permissionCheck('ticket.agent')
|
||||||
|
|
||||||
tickets_count = user.lifetimeCustomerTicketsCount()
|
tickets_count = user.lifetimeCustomerTicketsCount()
|
||||||
@html App.view('customer_not_ticket_exists')(has_any_tickets: tickets_count > 0)
|
@html App.view('customer_not_ticket_exists')(has_any_tickets: tickets_count > 0, is_allowed_to_create_ticket: @Config.get('customer_ticket_create'))
|
||||||
|
|
||||||
if tickets_count == 0
|
if tickets_count == 0
|
||||||
@listenTo user, 'refresh', =>
|
@listenTo user, 'refresh', =>
|
||||||
|
|
|
@ -200,10 +200,10 @@ class App.TicketZoom extends App.Controller
|
||||||
formMeta = data.form_meta
|
formMeta = data.form_meta
|
||||||
|
|
||||||
# on the following states we want to rerender the ticket:
|
# on the following states we want to rerender the ticket:
|
||||||
# - if the object attribute configuration has changed (attribute values, restrictions, filters)
|
# - if the object attribute configuration has changed (attribute values, dependecies, filters)
|
||||||
# - if the user view has changed (agent/customer)
|
# - if the user view has changed (agent/customer)
|
||||||
# - if the ticket permission has changed (read/write/full)
|
# - if the ticket permission has changed (read/write/full)
|
||||||
if @view && ( !_.isEqual(@formMeta, formMeta) || @view isnt view || @readable isnt readable || @changeable isnt changeable || @fullable isnt fullable )
|
if @view && ( !_.isEqual(@formMeta.configure_attributes, formMeta.configure_attributes) || !_.isEqual(@formMeta.dependencies, formMeta.dependencies) || !_.isEqual(@formMeta.filter, formMeta.filter) || @view isnt view || @readable isnt readable || @changeable isnt changeable || @fullable isnt fullable )
|
||||||
@renderDone = false
|
@renderDone = false
|
||||||
|
|
||||||
@view = view
|
@view = view
|
||||||
|
@ -214,6 +214,7 @@ class App.TicketZoom extends App.Controller
|
||||||
|
|
||||||
# render page
|
# render page
|
||||||
@render(local)
|
@render(local)
|
||||||
|
App.Event.trigger('ui::ticket::load', data)
|
||||||
|
|
||||||
meta: =>
|
meta: =>
|
||||||
|
|
||||||
|
@ -492,16 +493,22 @@ class App.TicketZoom extends App.Controller
|
||||||
@form_id = @taskGet('article').form_id || App.ControllerForm.formId()
|
@form_id = @taskGet('article').form_id || App.ControllerForm.formId()
|
||||||
|
|
||||||
@articleNew = new App.TicketZoomArticleNew(
|
@articleNew = new App.TicketZoomArticleNew(
|
||||||
ticket: @ticket
|
ticket: @ticket
|
||||||
ticket_id: @ticket_id
|
ticket_id: @ticket_id
|
||||||
el: elLocal.find('.article-new')
|
el: elLocal.find('.article-new')
|
||||||
formMeta: @formMeta
|
formMeta: @formMeta
|
||||||
form_id: @form_id
|
form_id: @form_id
|
||||||
defaults: @taskGet('article')
|
defaults: @taskGet('article')
|
||||||
taskKey: @taskKey
|
taskKey: @taskKey
|
||||||
ui: @
|
ui: @
|
||||||
callbackFileUploadStart: @submitDisable
|
richTextUploadStartCallback: @submitDisable
|
||||||
callbackFileUploadStop: @submitEnable
|
richTextUploadRenderCallback: (attachments) =>
|
||||||
|
@submitEnable()
|
||||||
|
@taskUpdateAttachments('article', attachments)
|
||||||
|
@delay(@markForm, 250, 'ticket-zoom-form-update')
|
||||||
|
richTextUploadDeleteCallback: (attachments) =>
|
||||||
|
@taskUpdateAttachments('article', attachments)
|
||||||
|
@delay(@markForm, 250, 'ticket-zoom-form-update')
|
||||||
)
|
)
|
||||||
|
|
||||||
@highligher = new App.TicketZoomHighlighter(
|
@highligher = new App.TicketZoomHighlighter(
|
||||||
|
@ -640,6 +647,7 @@ class App.TicketZoom extends App.Controller
|
||||||
# update changes in ui
|
# update changes in ui
|
||||||
currentStore = @currentStore()
|
currentStore = @currentStore()
|
||||||
modelDiff = @formDiff(currentParams, currentStore)
|
modelDiff = @formDiff(currentParams, currentStore)
|
||||||
|
return if _.isEmpty(modelDiff)
|
||||||
|
|
||||||
# set followup state if needed
|
# set followup state if needed
|
||||||
@setDefaultFollowUpState(modelDiff, currentStore)
|
@setDefaultFollowUpState(modelDiff, currentStore)
|
||||||
|
@ -719,7 +727,7 @@ class App.TicketZoom extends App.Controller
|
||||||
# add attachments if exist
|
# add attachments if exist
|
||||||
attachmentCount = @$('.article-add .textBubble .attachments .attachment').length
|
attachmentCount = @$('.article-add .textBubble .attachments .attachment').length
|
||||||
if attachmentCount > 0
|
if attachmentCount > 0
|
||||||
currentParams.article.attachments = true
|
currentParams.article.attachments = attachmentCount
|
||||||
else
|
else
|
||||||
delete currentParams.article.attachments
|
delete currentParams.article.attachments
|
||||||
|
|
||||||
|
@ -734,6 +742,14 @@ class App.TicketZoom extends App.Controller
|
||||||
|
|
||||||
# do not compare null or undefined value
|
# do not compare null or undefined value
|
||||||
if currentStore.ticket
|
if currentStore.ticket
|
||||||
|
|
||||||
|
# make sure that the compared state is same in local storage and
|
||||||
|
# rendered html. Else we could have race conditions of data
|
||||||
|
# which is not rendered yet
|
||||||
|
renderedUpdatedAt = @el.find('.edit').attr('data-ticket-updated-at')
|
||||||
|
return if !renderedUpdatedAt
|
||||||
|
return if currentStore.ticket.updated_at.toString() isnt renderedUpdatedAt
|
||||||
|
|
||||||
for key, value of currentStore.ticket
|
for key, value of currentStore.ticket
|
||||||
if value is null || value is undefined
|
if value is null || value is undefined
|
||||||
currentStore.ticket[key] = ''
|
currentStore.ticket[key] = ''
|
||||||
|
@ -968,15 +984,14 @@ class App.TicketZoom extends App.Controller
|
||||||
processData: true
|
processData: true
|
||||||
success: (data) =>
|
success: (data) =>
|
||||||
|
|
||||||
#App.SessionStorage.set(@key, data)
|
|
||||||
@load(data, true, true)
|
|
||||||
|
|
||||||
# reset article - should not be resubmitted on next ticket update
|
# reset article - should not be resubmitted on next ticket update
|
||||||
ticket.article = undefined
|
ticket.article = undefined
|
||||||
|
|
||||||
# reset form after save
|
# reset form after save
|
||||||
@reset()
|
@reset()
|
||||||
|
|
||||||
|
@load(data, false, true)
|
||||||
|
|
||||||
if @sidebarWidget
|
if @sidebarWidget
|
||||||
@sidebarWidget.commit()
|
@sidebarWidget.commit()
|
||||||
|
|
||||||
|
@ -1069,6 +1084,13 @@ class App.TicketZoom extends App.Controller
|
||||||
|
|
||||||
App.TaskManager.update(@taskKey, taskData)
|
App.TaskManager.update(@taskKey, taskData)
|
||||||
|
|
||||||
|
taskUpdateAttachments: (area, attachments) =>
|
||||||
|
taskData = App.TaskManager.get(@taskKey)
|
||||||
|
return if !taskData
|
||||||
|
|
||||||
|
taskData.attachments = attachments
|
||||||
|
App.TaskManager.update(@taskKey, taskData)
|
||||||
|
|
||||||
taskUpdateAll: (data) =>
|
taskUpdateAll: (data) =>
|
||||||
@localTaskData = data
|
@localTaskData = data
|
||||||
@localTaskData.article['form_id'] = @form_id
|
@localTaskData.article['form_id'] = @form_id
|
||||||
|
@ -1091,7 +1113,7 @@ class App.TicketZoom extends App.Controller
|
||||||
@localTaskData =
|
@localTaskData =
|
||||||
ticket: {}
|
ticket: {}
|
||||||
article: {}
|
article: {}
|
||||||
App.TaskManager.update(@taskKey, { 'state': @localTaskData })
|
App.TaskManager.update(@taskKey, { 'state': @localTaskData, attachments: [] })
|
||||||
|
|
||||||
renderOverviewNavigator: (parentEl) ->
|
renderOverviewNavigator: (parentEl) ->
|
||||||
new App.TicketZoomOverviewNavigator(
|
new App.TicketZoomOverviewNavigator(
|
||||||
|
|
|
@ -257,7 +257,7 @@ class EmailReply extends App.Controller
|
||||||
|
|
||||||
quote_header = App.FullQuoteHeader.fullQuoteHeaderForward(article)
|
quote_header = App.FullQuoteHeader.fullQuoteHeaderForward(article)
|
||||||
|
|
||||||
body = "<br/><div>---Begin forwarded message:---<br/><br/></div><div><blockquote type=\"cite\">#{quote_header}#{body}</blockquote></div><div><br></div>"
|
body = "<br/><div>---#{App.i18n.translateInline('Begin forwarded message')}:---<br/><br/></div><div><blockquote type=\"cite\">#{quote_header}#{body}</blockquote></div><div><br></div>"
|
||||||
|
|
||||||
articleNew = {}
|
articleNew = {}
|
||||||
articleNew.body = body
|
articleNew.body = body
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Internal
|
||||||
internal = true
|
internal = true
|
||||||
if article.internal == true
|
if article.internal == true
|
||||||
internal = false
|
internal = false
|
||||||
ui.lastAttributres.internal = internal
|
ui.lastAttributes.internal = internal
|
||||||
article.updateAttributes(internal: internal)
|
article.updateAttributes(internal: internal)
|
||||||
|
|
||||||
# runtime update
|
# runtime update
|
||||||
|
|
|
@ -98,7 +98,7 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
@controllerBind('ui:rerender', =>
|
@controllerBind('ui:rerender', =>
|
||||||
@adjustedTextarea = false
|
@adjustedTextarea = false
|
||||||
@defaults = @ui.taskGet('article')
|
@defaults = @ui.taskGet('article')
|
||||||
@attachments = @defaults.attachments
|
@attachments = @defaults.attachments || []
|
||||||
@render()
|
@render()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
|
|
||||||
@tokanice(@type)
|
@tokanice(@type)
|
||||||
|
|
||||||
if @defaults.body or @isIE10()
|
if @defaults.body or @attachments.length > 0 or @isIE10()
|
||||||
@openTextarea(null, true)
|
@openTextarea(null, true)
|
||||||
|
|
||||||
tokanice: (type = 'email') ->
|
tokanice: (type = 'email') ->
|
||||||
|
@ -191,82 +191,30 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
maxlength: 150000
|
maxlength: 150000
|
||||||
})
|
})
|
||||||
|
|
||||||
html5Upload.initialize(
|
new App.Html5Upload(
|
||||||
uploadUrl: "#{App.Config.get('api_path')}/upload_caches/#{@form_id}"
|
uploadUrl: "#{App.Config.get('api_path')}/upload_caches/#{@form_id}"
|
||||||
dropContainer: @$('.article-add').get(0)
|
dropContainer: @$('.article-add')
|
||||||
cancelContainer: @cancelContainer
|
cancelContainer: @cancelContainer
|
||||||
inputField: @$('.article-attachment input').get(0)
|
inputField: @$('.article-attachment input')
|
||||||
key: 'File'
|
|
||||||
maxSimultaneousUploads: 1
|
|
||||||
onFileAdded: (file) =>
|
|
||||||
|
|
||||||
file.on(
|
onFileStartCallback: =>
|
||||||
|
@richTextUploadStartCallback?()
|
||||||
|
|
||||||
onStart: =>
|
onFileCompletedCallback: (response) =>
|
||||||
@attachmentPlaceholder.addClass('hide')
|
@attachments.push response.data
|
||||||
@attachmentUpload.removeClass('hide')
|
@renderAttachment(response.data)
|
||||||
@cancelContainer.removeClass('hide')
|
@$('.article-attachment input').val('')
|
||||||
|
|
||||||
if @callbackFileUploadStart
|
@richTextUploadRenderCallback?(@attachments)
|
||||||
@callbackFileUploadStart()
|
|
||||||
|
|
||||||
onAborted: =>
|
onFileAbortedCallback: =>
|
||||||
@attachmentPlaceholder.removeClass('hide')
|
@richTextUploadRenderCallback?(@attachments)
|
||||||
@attachmentUpload.addClass('hide')
|
|
||||||
@$('.article-attachment input').val('')
|
|
||||||
|
|
||||||
if @callbackFileUploadStop
|
attachmentPlaceholder: @attachmentPlaceholder
|
||||||
@callbackFileUploadStop()
|
attachmentUpload: @attachmentUpload
|
||||||
|
progressBar: @progressBar
|
||||||
# Called after received response from the server
|
progressText: @progressText
|
||||||
onCompleted: (response) =>
|
).render()
|
||||||
|
|
||||||
response = JSON.parse(response)
|
|
||||||
@attachments.push response.data
|
|
||||||
|
|
||||||
@attachmentPlaceholder.removeClass('hide')
|
|
||||||
@attachmentUpload.addClass('hide')
|
|
||||||
|
|
||||||
# reset progress bar
|
|
||||||
@progressBar.width(parseInt(0) + '%')
|
|
||||||
@progressText.text('')
|
|
||||||
|
|
||||||
@renderAttachment(response.data)
|
|
||||||
@$('.article-attachment input').val('')
|
|
||||||
|
|
||||||
if @callbackFileUploadStop
|
|
||||||
@callbackFileUploadStop()
|
|
||||||
|
|
||||||
# Called during upload progress, first parameter
|
|
||||||
# is decimal value from 0 to 100.
|
|
||||||
onProgress: (progress, fileSize, uploadedBytes) =>
|
|
||||||
@progressBar.width(parseInt(progress) + '%')
|
|
||||||
@progressText.text(parseInt(progress))
|
|
||||||
# hide cancel on 90%
|
|
||||||
if parseInt(progress) >= 90
|
|
||||||
@cancelContainer.addClass('hide')
|
|
||||||
|
|
||||||
# Called when upload failed
|
|
||||||
onError: (message) =>
|
|
||||||
@attachmentPlaceholder.removeClass('hide')
|
|
||||||
@attachmentUpload.addClass('hide')
|
|
||||||
@$('.article-attachment input').val('')
|
|
||||||
|
|
||||||
if @callbackFileUploadStop
|
|
||||||
@callbackFileUploadStop()
|
|
||||||
|
|
||||||
new App.ControllerModal(
|
|
||||||
head: 'Upload Failed'
|
|
||||||
buttonCancel: 'Cancel'
|
|
||||||
buttonCancelClass: 'btn--danger'
|
|
||||||
buttonSubmit: false
|
|
||||||
message: message
|
|
||||||
shown: true
|
|
||||||
small: true
|
|
||||||
container: @el.closest('.content')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@bindAttachmentDelete()
|
@bindAttachmentDelete()
|
||||||
|
|
||||||
|
@ -339,7 +287,6 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
params.preferences ||= {}
|
params.preferences ||= {}
|
||||||
params.preferences.security = @paramsSecurity()
|
params.preferences.security = @paramsSecurity()
|
||||||
|
|
||||||
params.attachments = @attachments
|
|
||||||
params
|
params
|
||||||
|
|
||||||
validate: =>
|
validate: =>
|
||||||
|
@ -676,6 +623,8 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
$(e.currentTarget).closest('.attachment').remove()
|
$(e.currentTarget).closest('.attachment').remove()
|
||||||
if element.find('.attachment').length == 0
|
if element.find('.attachment').length == 0
|
||||||
element.empty()
|
element.empty()
|
||||||
|
|
||||||
|
@richTextUploadDeleteCallback?(@attachments)
|
||||||
)
|
)
|
||||||
|
|
||||||
actions: ->
|
actions: ->
|
||||||
|
|
|
@ -218,11 +218,11 @@ class ArticleViewItem extends App.ControllerObserver
|
||||||
)
|
)
|
||||||
|
|
||||||
@articleActions = new App.TicketZoomArticleActions(
|
@articleActions = new App.TicketZoomArticleActions(
|
||||||
el: @$('.js-article-actions')
|
el: @$('.js-article-actions')
|
||||||
ticket: @ticket
|
ticket: @ticket
|
||||||
article: article
|
article: article
|
||||||
lastAttributres: @lastAttributres
|
lastAttributes: @lastAttributes
|
||||||
form_id: @form_id
|
form_id: @form_id
|
||||||
)
|
)
|
||||||
|
|
||||||
# set see more
|
# set see more
|
||||||
|
@ -305,34 +305,38 @@ class ArticleViewItem extends App.ControllerObserver
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
article_id = $(e.target).closest('.ticket-article-item').data('id')
|
article_id = $(e.target).closest('.ticket-article-item').data('id')
|
||||||
|
article = App.TicketArticle.find(article_id)
|
||||||
|
|
||||||
@ajax(
|
@ajax(
|
||||||
id: 'retrySecurityProcess'
|
id: 'retrySecurityProcess'
|
||||||
type: 'POST'
|
type: 'POST'
|
||||||
url: "#{@apiPath}/ticket_articles/#{article_id}/retry_security_process"
|
url: "#{@apiPath}/ticket_articles/#{article_id}/retry_security_process"
|
||||||
processData: true
|
processData: true
|
||||||
success: (data, status, xhr) =>
|
success: (encryption_data, status, xhr) =>
|
||||||
if data.sign.success
|
for data in encryption_data
|
||||||
@notify
|
continue if article.preferences.security.type isnt data.type
|
||||||
type: 'success'
|
|
||||||
msg: App.i18n.translateContent('Verify sign success!')
|
|
||||||
else if data.sign.comment
|
|
||||||
comment = App.i18n.translateContent('Verify sign failed!') + ' ' + App.i18n.translateContent(data.sign.comment || '')
|
|
||||||
@notify
|
|
||||||
type: 'error'
|
|
||||||
msg: comment
|
|
||||||
timeout: 2000
|
|
||||||
|
|
||||||
if data.encryption.success
|
if data.sign.success
|
||||||
@notify
|
@notify
|
||||||
type: 'success'
|
type: 'success'
|
||||||
msg: App.i18n.translateContent('Decryption success!')
|
msg: App.i18n.translateContent('Verify sign success!')
|
||||||
else if data.encryption.comment
|
else if data.sign.comment
|
||||||
comment = App.i18n.translateContent('Decryption failed!') + ' ' + App.i18n.translateContent(data.encryption.comment || '')
|
comment = App.i18n.translateContent('Verify sign failed!') + ' ' + App.i18n.translateContent(data.sign.comment || '')
|
||||||
@notify
|
@notify
|
||||||
type: 'error'
|
type: 'error'
|
||||||
msg: comment
|
msg: comment
|
||||||
timeout: 2000
|
timeout: 2000
|
||||||
|
|
||||||
|
if data.encryption.success
|
||||||
|
@notify
|
||||||
|
type: 'success'
|
||||||
|
msg: App.i18n.translateContent('Decryption success!')
|
||||||
|
else if data.encryption.comment
|
||||||
|
comment = App.i18n.translateContent('Decryption failed!') + ' ' + App.i18n.translateContent(data.encryption.comment || '')
|
||||||
|
@notify
|
||||||
|
type: 'error'
|
||||||
|
msg: comment
|
||||||
|
timeout: 2000
|
||||||
|
|
||||||
error: (xhr) =>
|
error: (xhr) =>
|
||||||
@notify
|
@notify
|
||||||
|
|
|
@ -79,6 +79,12 @@ class App.FormHandlerCoreWorkflow
|
||||||
return if !App.WebSocket.channel()
|
return if !App.WebSocket.channel()
|
||||||
return !App.Config.get('core_workflow_ajax_mode')
|
return !App.Config.get('core_workflow_ajax_mode')
|
||||||
|
|
||||||
|
@restrictValuesAttributeCache: (attribute, values) ->
|
||||||
|
result = { values: values }
|
||||||
|
return result if !attribute.relation
|
||||||
|
result.lastUpdatedAt = App[attribute.relation].lastUpdatedAt()
|
||||||
|
return result
|
||||||
|
|
||||||
# restricts the dropdown and tree select values of a form
|
# restricts the dropdown and tree select values of a form
|
||||||
@restrictValues: (classname, form, ui, attributes, params, data) ->
|
@restrictValues: (classname, form, ui, attributes, params, data) ->
|
||||||
return if _.isEmpty(data.restrict_values)
|
return if _.isEmpty(data.restrict_values)
|
||||||
|
@ -111,15 +117,17 @@ class App.FormHandlerCoreWorkflow
|
||||||
# cache state for performance and only run
|
# cache state for performance and only run
|
||||||
# if values or param differ
|
# if values or param differ
|
||||||
if coreWorkflowRestrictions?[classname]?[item.name]
|
if coreWorkflowRestrictions?[classname]?[item.name]
|
||||||
compare = values
|
compare = App.FormHandlerCoreWorkflow.restrictValuesAttributeCache(attribute, values)
|
||||||
continue if _.isEqual(coreWorkflowRestrictions[classname][item.name], compare)
|
continue if _.isEqual(coreWorkflowRestrictions[classname][item.name], compare)
|
||||||
|
|
||||||
coreWorkflowRestrictions[classname] ||= {}
|
coreWorkflowRestrictions[classname] ||= {}
|
||||||
coreWorkflowRestrictions[classname][item.name] = values
|
coreWorkflowRestrictions[classname][item.name] = App.FormHandlerCoreWorkflow.restrictValuesAttributeCache(attribute, values)
|
||||||
|
|
||||||
valueFound = false
|
valueFound = false
|
||||||
for value in values
|
for value in values
|
||||||
if value && paramValue
|
|
||||||
|
# false values are valid values e.g. for boolean fields (be careful)
|
||||||
|
if value isnt undefined && paramValue isnt undefined && value isnt null && paramValue isnt null
|
||||||
if value.toString() == paramValue.toString()
|
if value.toString() == paramValue.toString()
|
||||||
valueFound = true
|
valueFound = true
|
||||||
break
|
break
|
||||||
|
@ -131,6 +139,11 @@ class App.FormHandlerCoreWorkflow
|
||||||
if valueFound
|
if valueFound
|
||||||
item.default = paramValue
|
item.default = paramValue
|
||||||
item.newValue = paramValue
|
item.newValue = paramValue
|
||||||
|
else if params.id
|
||||||
|
obj = App[ui.model.className].find(params.id)
|
||||||
|
if obj && obj[item.name]
|
||||||
|
item.default = obj[item.name]
|
||||||
|
item.newValue = obj[item.name]
|
||||||
else
|
else
|
||||||
item.default = ''
|
item.default = ''
|
||||||
item.newValue = ''
|
item.newValue = ''
|
||||||
|
@ -297,6 +310,11 @@ class App.FormHandlerCoreWorkflow
|
||||||
screen: ui.screen
|
screen: ui.screen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# send last changed attribute only once for has changed condition
|
||||||
|
if ui.lastChangedAttribute
|
||||||
|
requestData.last_changed_attribute = ui.lastChangedAttribute
|
||||||
|
ui.lastChangedAttribute = '-'
|
||||||
|
|
||||||
if App.FormHandlerCoreWorkflow.useWebSockets()
|
if App.FormHandlerCoreWorkflow.useWebSockets()
|
||||||
App.WebSocket.send(requestData)
|
App.WebSocket.send(requestData)
|
||||||
else
|
else
|
||||||
|
@ -322,8 +340,12 @@ class App.FormHandlerCoreWorkflow
|
||||||
|
|
||||||
# get params and add id from ui if needed
|
# get params and add id from ui if needed
|
||||||
params = App.FormHandlerCoreWorkflow.cleanParams(params_ref)
|
params = App.FormHandlerCoreWorkflow.cleanParams(params_ref)
|
||||||
if ui?.params?.id
|
|
||||||
|
# add object id for edit screens
|
||||||
|
if ui?.params?.id && ui.screen.match(/edit/)
|
||||||
params.id = ui.params.id
|
params.id = ui.params.id
|
||||||
|
else
|
||||||
|
delete params.id
|
||||||
|
|
||||||
# skip double checks
|
# skip double checks
|
||||||
return if _.isEqual(coreWorkflowParams[classname], params)
|
return if _.isEqual(coreWorkflowParams[classname], params)
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
class Edit extends App.ControllerObserver
|
# No usage of a ControllerObserver here because we want to use
|
||||||
model: 'Ticket'
|
# the data of the ticket zoom ajax request which is using the all=true parameter
|
||||||
observeNot:
|
# and contain the core workflow information as well. Without observer we also
|
||||||
created_at: true
|
# dont have double rendering because of the zoom (all=true) and observer (full=true) render callback
|
||||||
updated_at: true
|
class Edit extends App.Controller
|
||||||
globalRerender: false
|
constructor: (params) ->
|
||||||
|
super
|
||||||
|
@controllerBind('ui::ticket::load', (data) =>
|
||||||
|
return if data.ticket_id.toString() isnt @ticket.id.toString()
|
||||||
|
|
||||||
render: (ticket, diff) =>
|
@ticket = App.Ticket.find(@ticket.id)
|
||||||
defaults = ticket.attributes()
|
@formMeta = data.form_meta
|
||||||
|
@render()
|
||||||
|
)
|
||||||
|
@render()
|
||||||
|
|
||||||
|
render: =>
|
||||||
|
defaults = @ticket.attributes()
|
||||||
delete defaults.article # ignore article infos
|
delete defaults.article # ignore article infos
|
||||||
followUpPossible = App.Group.find(defaults.group_id).follow_up_possible
|
followUpPossible = App.Group.find(defaults.group_id).follow_up_possible
|
||||||
ticketState = App.TicketState.find(defaults.state_id).name
|
ticketState = App.TicketState.find(defaults.state_id).name
|
||||||
|
@ -16,49 +25,44 @@ class Edit extends App.ControllerObserver
|
||||||
|
|
||||||
if !_.isEmpty(taskState)
|
if !_.isEmpty(taskState)
|
||||||
defaults = _.extend(defaults, taskState)
|
defaults = _.extend(defaults, taskState)
|
||||||
|
# remove core workflow data because it should trigger a request to get data
|
||||||
|
# for the new ticket + eventually changed task state
|
||||||
|
@formMeta.core_workflow = undefined
|
||||||
|
|
||||||
if followUpPossible == 'new_ticket' && ticketState != 'closed' ||
|
editable = @ticket.editable()
|
||||||
followUpPossible != 'new_ticket' ||
|
if followUpPossible == 'new_ticket' && ticketState != 'closed' || followUpPossible != 'new_ticket' || @permissionCheck('admin') || @ticket.currentView() is 'agent'
|
||||||
@permissionCheck('admin') || ticket.currentView() is 'agent'
|
editable = !editable
|
||||||
@controllerFormSidebarTicket = new App.ControllerForm(
|
|
||||||
elReplace: @el
|
|
||||||
model: { className: 'Ticket', configure_attributes: @formMeta.configure_attributes || App.Ticket.configure_attributes }
|
|
||||||
screen: 'edit'
|
|
||||||
handlersConfig: handlers
|
|
||||||
filter: @formMeta.filter
|
|
||||||
formMeta: @formMeta
|
|
||||||
params: defaults
|
|
||||||
isDisabled: !ticket.editable()
|
|
||||||
taskKey: @taskKey
|
|
||||||
core_workflow: {
|
|
||||||
callbacks: [@markForm]
|
|
||||||
}
|
|
||||||
#bookmarkable: true
|
|
||||||
)
|
|
||||||
else
|
|
||||||
@controllerFormSidebarTicket = new App.ControllerForm(
|
|
||||||
elReplace: @el
|
|
||||||
model: { className: 'Ticket', configure_attributes: @formMeta.configure_attributes || App.Ticket.configure_attributes }
|
|
||||||
screen: 'edit'
|
|
||||||
handlersConfig: handlers
|
|
||||||
filter: @formMeta.filter
|
|
||||||
formMeta: @formMeta
|
|
||||||
params: defaults
|
|
||||||
isDisabled: ticket.editable()
|
|
||||||
taskKey: @taskKey
|
|
||||||
core_workflow: {
|
|
||||||
callbacks: [@markForm]
|
|
||||||
}
|
|
||||||
#bookmarkable: true
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# reset updated_at for the sidbar because we render a new state
|
||||||
|
# it is used to compare the ticket with the rendered data later
|
||||||
|
# and needed to prevent race conditions
|
||||||
|
@el.removeAttr('data-ticket-updated-at')
|
||||||
|
|
||||||
|
@controllerFormSidebarTicket = new App.ControllerForm(
|
||||||
|
elReplace: @el
|
||||||
|
model: { className: 'Ticket', configure_attributes: @formMeta.configure_attributes || App.Ticket.configure_attributes }
|
||||||
|
screen: 'edit'
|
||||||
|
handlersConfig: handlers
|
||||||
|
filter: @formMeta.filter
|
||||||
|
formMeta: @formMeta
|
||||||
|
params: defaults
|
||||||
|
isDisabled: editable
|
||||||
|
taskKey: @taskKey
|
||||||
|
core_workflow: {
|
||||||
|
callbacks: [@markForm]
|
||||||
|
}
|
||||||
|
#bookmarkable: true
|
||||||
|
)
|
||||||
|
|
||||||
|
# set updated_at for the sidbar because we render a new state
|
||||||
|
@el.attr('data-ticket-updated-at', defaults.updated_at)
|
||||||
@markForm(true)
|
@markForm(true)
|
||||||
|
|
||||||
return if @resetBind
|
return if @resetBind
|
||||||
@resetBind = true
|
@resetBind = true
|
||||||
@controllerBind('ui::ticket::taskReset', (data) =>
|
@controllerBind('ui::ticket::taskReset', (data) =>
|
||||||
return if data.ticket_id.toString() isnt ticket.id.toString()
|
return if data.ticket_id.toString() isnt @ticket.id.toString()
|
||||||
@render(ticket)
|
@render()
|
||||||
)
|
)
|
||||||
|
|
||||||
class SidebarTicket extends App.Controller
|
class SidebarTicket extends App.Controller
|
||||||
|
@ -128,6 +132,7 @@ class SidebarTicket extends App.Controller
|
||||||
|
|
||||||
@edit = new Edit(
|
@edit = new Edit(
|
||||||
object_id: @ticket.id
|
object_id: @ticket.id
|
||||||
|
ticket: @ticket
|
||||||
el: localEl.find('.edit')
|
el: localEl.find('.edit')
|
||||||
taskGet: @taskGet
|
taskGet: @taskGet
|
||||||
formMeta: @formMeta
|
formMeta: @formMeta
|
||||||
|
|
|
@ -19,7 +19,7 @@ class App.TicketZoomTitle extends App.ControllerObserver
|
||||||
title = $(e.target).ceg() || ''
|
title = $(e.target).ceg() || ''
|
||||||
|
|
||||||
# update title
|
# update title
|
||||||
return if title is @lastAttributres.title
|
return if title is @lastAttributes.title
|
||||||
ticket = App.Ticket.find(@object_id)
|
ticket = App.Ticket.find(@object_id)
|
||||||
ticket.title = title
|
ticket.title = title
|
||||||
|
|
||||||
|
|
|
@ -220,7 +220,7 @@ class Object extends App.ControllerObserver
|
||||||
value = $(e.target).html()
|
value = $(e.target).html()
|
||||||
user = App.User.find(@object_id)
|
user = App.User.find(@object_id)
|
||||||
if user[name] isnt value
|
if user[name] isnt value
|
||||||
@lastAttributres[name] = value
|
@lastAttributes[name] = value
|
||||||
data = {}
|
data = {}
|
||||||
data[name] = value
|
data[name] = value
|
||||||
user.updateAttributes(data)
|
user.updateAttributes(data)
|
||||||
|
|
|
@ -33,7 +33,7 @@ class App.WidgetOrganization extends App.Controller
|
||||||
elLocal.find('.js-userList').html(members)
|
elLocal.find('.js-userList').html(members)
|
||||||
)
|
)
|
||||||
|
|
||||||
if @organization.member_ids.length < @memberLimit
|
if @organization.member_ids.length <= @memberLimit
|
||||||
@el.find('.js-showMoreMembers').parent().addClass('hidden')
|
@el.find('.js-showMoreMembers').parent().addClass('hidden')
|
||||||
else
|
else
|
||||||
@el.find('.js-showMoreMembers').parent().removeClass('hidden')
|
@el.find('.js-showMoreMembers').parent().removeClass('hidden')
|
||||||
|
|
|
@ -14,6 +14,7 @@ class App.ObjectOrganizationAutocompletion extends App.Controller
|
||||||
'click': 'stopPropagation'
|
'click': 'stopPropagation'
|
||||||
'change .js-objectId': 'executeCallback'
|
'change .js-objectId': 'executeCallback'
|
||||||
'click .js-remove': 'removeThisToken'
|
'click .js-remove': 'removeThisToken'
|
||||||
|
'click .js-showMoreMembers': 'showMoreMembers'
|
||||||
|
|
||||||
elements:
|
elements:
|
||||||
'.recipientList': 'recipientList'
|
'.recipientList': 'recipientList'
|
||||||
|
@ -251,14 +252,42 @@ class App.ObjectOrganizationAutocompletion extends App.Controller
|
||||||
objectCount: objectCount
|
objectCount: objectCount
|
||||||
)
|
)
|
||||||
|
|
||||||
|
showMoreMembers: (e) ->
|
||||||
|
@preventDefaultAndStopPropagation(e)
|
||||||
|
|
||||||
|
memberElement = $(e.target).closest('.js-showMoreMembers')
|
||||||
|
oldMemberLimit = memberElement.attr('organization-member-limit')
|
||||||
|
newMemberLimit = (parseInt(oldMemberLimit / 25) + 1) * 25
|
||||||
|
memberElement.attr('organization-member-limit', newMemberLimit)
|
||||||
|
|
||||||
|
@renderMembers(memberElement, oldMemberLimit, newMemberLimit)
|
||||||
|
|
||||||
|
renderMembers: (element, fromMemberLimit, toMemberLimit) ->
|
||||||
|
id = element.closest('.recipientList-organizationMembers').attr('organization-id')
|
||||||
|
organization = App.Organization.find(id)
|
||||||
|
|
||||||
|
# only first 10 members else we would need more ajax requests
|
||||||
|
organization.members(fromMemberLimit, toMemberLimit, (users) =>
|
||||||
|
for user in users
|
||||||
|
element.before(@buildObjectItem(user))
|
||||||
|
|
||||||
|
if element.closest('ul').hasClass('is-shown')
|
||||||
|
@showOrganizationMembers(undefined, element.closest('ul'))
|
||||||
|
)
|
||||||
|
|
||||||
|
if organization.member_ids.length <= toMemberLimit
|
||||||
|
element.addClass('hidden')
|
||||||
|
else
|
||||||
|
element.removeClass('hidden')
|
||||||
|
|
||||||
buildOrganizationMembers: (organization) =>
|
buildOrganizationMembers: (organization) =>
|
||||||
organizationMemebers = $( App.view(@templateOrganizationItemMembers)(
|
organizationMembers = $( App.view(@templateOrganizationItemMembers)(
|
||||||
organization: organization
|
organization: organization
|
||||||
) )
|
) )
|
||||||
if organization[@referenceAttribute]
|
|
||||||
for objectId in organization[@referenceAttribute]
|
@renderMembers(organizationMembers.find('.js-showMoreMembers'), 0, 10)
|
||||||
object = App[@objectSingle].fullLocal(objectId)
|
|
||||||
organizationMemebers.append(@buildObjectItem(object))
|
organizationMembers
|
||||||
|
|
||||||
buildObjectItem: (object) =>
|
buildObjectItem: (object) =>
|
||||||
icon = @objectIcon
|
icon = @objectIcon
|
||||||
|
@ -404,8 +433,7 @@ class App.ObjectOrganizationAutocompletion extends App.Controller
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
listEntry = $(e.currentTarget)
|
listEntry = $(e.currentTarget)
|
||||||
|
|
||||||
organizationId = listEntry.data('organization-id')
|
organizationId = listEntry.data('organization-id') || listEntry.attr('organization-id')
|
||||||
|
|
||||||
@organizationList = @$("[organization-id=#{ organizationId }]")
|
@organizationList = @$("[organization-id=#{ organizationId }]")
|
||||||
|
|
||||||
return if !@organizationList.get(0)
|
return if !@organizationList.get(0)
|
||||||
|
|
|
@ -102,12 +102,18 @@ class _ajaxSingleton
|
||||||
# do not show any error message with code 502
|
# do not show any error message with code 502
|
||||||
return if status is 502
|
return if status is 502
|
||||||
|
|
||||||
|
try
|
||||||
|
json = JSON.parse(detail)
|
||||||
|
text = json.error_human || json.error
|
||||||
|
|
||||||
|
text = detail if !text
|
||||||
|
|
||||||
|
escaped = App.Utils.htmlEscape(text)
|
||||||
|
|
||||||
# show error message
|
# show error message
|
||||||
new App.ControllerModal(
|
new App.ControllerTechnicalErrorModal(
|
||||||
head: "StatusCode: #{status}"
|
contentCode: escaped
|
||||||
contentInline: "<pre>#{App.Utils.htmlEscape(detail)}</pre>"
|
head: "StatusCode: #{status}"
|
||||||
buttonClose: true
|
|
||||||
buttonSubmit: false
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class App.FullQuoteHeader
|
||||||
|
|
||||||
@fullQuoteHeaderForwardTo: (article) ->
|
@fullQuoteHeaderForwardTo: (article) ->
|
||||||
if article.type.name is 'email' || article.type.name is 'web'
|
if article.type.name is 'email' || article.type.name is 'web'
|
||||||
@fullQuoteHeaderEnsurePrivacy(article.to) || article.to
|
@fullQuoteHeaderEnsureMultiPrivacy(article.to)
|
||||||
else if article.sender.name is 'Customer' && article.type.name is 'phone'
|
else if article.sender.name is 'Customer' && article.type.name is 'phone'
|
||||||
if email_address_id = App.Group.findByAttribute('name', article.to)?.email_address_id
|
if email_address_id = App.Group.findByAttribute('name', article.to)?.email_address_id
|
||||||
App.EmailAddress.find(email_address_id).displayName()
|
App.EmailAddress.find(email_address_id).displayName()
|
||||||
|
@ -36,15 +36,17 @@ class App.FullQuoteHeader
|
||||||
article.to
|
article.to
|
||||||
else if article.sender.name is 'Agent' && article.type.name is 'phone'
|
else if article.sender.name is 'Agent' && article.type.name is 'phone'
|
||||||
ticket = App.Ticket.find article.ticket_id
|
ticket = App.Ticket.find article.ticket_id
|
||||||
@fullQuoteHeaderEnsurePrivacy(ticket.customer_id) || @fullQuoteHeaderEnsurePrivacy(article.to) || article.to
|
@fullQuoteHeaderEnsurePrivacy(ticket.customer_id) || @fullQuoteHeaderEnsureMultiPrivacy(article.to)
|
||||||
else
|
else
|
||||||
article.to
|
article.to
|
||||||
|
|
||||||
@fullQuoteHeaderForwardCC: (article) ->
|
@fullQuoteHeaderForwardCC: (article) ->
|
||||||
return if !article.cc
|
@fullQuoteHeaderEnsureMultiPrivacy(article.cc)
|
||||||
|
|
||||||
article
|
@fullQuoteHeaderEnsureMultiPrivacy: (input) ->
|
||||||
.cc
|
return if !input
|
||||||
|
|
||||||
|
input
|
||||||
.split(',')
|
.split(',')
|
||||||
.map (elem) ->
|
.map (elem) ->
|
||||||
elem.trim()
|
elem.trim()
|
||||||
|
|
98
app/assets/javascripts/app/lib/app_post/html5_upload.coffee
Normal file
98
app/assets/javascripts/app/lib/app_post/html5_upload.coffee
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
class App.Html5Upload extends App.Controller
|
||||||
|
uploadUrl: null
|
||||||
|
maxSimultaneousUploads: 1
|
||||||
|
key: 'File'
|
||||||
|
data: null
|
||||||
|
|
||||||
|
onFileStartCallback: null
|
||||||
|
onFileCompletedCallback: null
|
||||||
|
onFileAbortedCallback: null
|
||||||
|
|
||||||
|
dropContainer: null
|
||||||
|
cancelContainer: null
|
||||||
|
inputField: null
|
||||||
|
attachmentPlaceholder: null
|
||||||
|
attachmentUpload: null
|
||||||
|
progressBar: null
|
||||||
|
progressText: null
|
||||||
|
|
||||||
|
render: =>
|
||||||
|
html5Upload.initialize(
|
||||||
|
uploadUrl: @uploadUrl
|
||||||
|
dropContainer: @dropContainer.get(0)
|
||||||
|
cancelContainer: @cancelContainer
|
||||||
|
inputField: @inputField.get(0)
|
||||||
|
maxSimultaneousUploads: @maxSimultaneousUploads
|
||||||
|
key: @key
|
||||||
|
data: @data
|
||||||
|
onFileAdded: @onFileAdded
|
||||||
|
)
|
||||||
|
|
||||||
|
onFileAdded: (file) =>
|
||||||
|
file.on(
|
||||||
|
onStart: @onFileStart
|
||||||
|
onAborted: @onFileAborted
|
||||||
|
onCompleted: @onFileCompleted
|
||||||
|
onProgress: @onFileProgress
|
||||||
|
onError: @onFileError
|
||||||
|
)
|
||||||
|
|
||||||
|
onFileStart: =>
|
||||||
|
@attachmentPlaceholder.addClass('hide')
|
||||||
|
@attachmentUpload.removeClass('hide')
|
||||||
|
@cancelContainer.removeClass('hide')
|
||||||
|
|
||||||
|
App.Log.debug 'Html5Upload', 'upload start'
|
||||||
|
@onFileStartCallback?()
|
||||||
|
|
||||||
|
onFileProgress: (progress, fileSize, uploadedBytes) =>
|
||||||
|
progress = parseInt(progress)
|
||||||
|
|
||||||
|
@progressBar.width(progress + '%')
|
||||||
|
@progressText.text(progress)
|
||||||
|
# hide cancel on 90%
|
||||||
|
if progress >= 90
|
||||||
|
@cancelContainer.addClass('hide')
|
||||||
|
|
||||||
|
App.Log.debug 'Html5Upload', 'uploadProgress ', progress
|
||||||
|
|
||||||
|
|
||||||
|
onFileCompleted: (response) =>
|
||||||
|
response = JSON.parse(response)
|
||||||
|
|
||||||
|
@hideFileUploading()
|
||||||
|
@onFileCompletedCallback?(response)
|
||||||
|
|
||||||
|
App.Log.debug 'Html5Upload', 'upload complete', response.data
|
||||||
|
|
||||||
|
onFileAborted: =>
|
||||||
|
@hideFileUploading()
|
||||||
|
@onFileAbortedCallback?()
|
||||||
|
|
||||||
|
App.Log.debug 'Html5Upload', 'upload aborted'
|
||||||
|
|
||||||
|
onFileError: (message) =>
|
||||||
|
@hideFileUploading()
|
||||||
|
@inputField.val('')
|
||||||
|
|
||||||
|
@callbackFileUploadStop?()
|
||||||
|
|
||||||
|
new App.ControllerModal(
|
||||||
|
head: 'Upload Failed'
|
||||||
|
buttonCancel: 'Cancel'
|
||||||
|
buttonCancelClass: 'btn--danger'
|
||||||
|
buttonSubmit: false
|
||||||
|
message: message || 'Cannot upload file'
|
||||||
|
shown: true
|
||||||
|
small: true
|
||||||
|
container: @inputField.closest('.content')
|
||||||
|
)
|
||||||
|
|
||||||
|
App.Log.debug 'Html5Upload', 'upload error'
|
||||||
|
|
||||||
|
hideFileUploading: =>
|
||||||
|
@attachmentPlaceholder.removeClass('hide')
|
||||||
|
@attachmentUpload.addClass('hide')
|
||||||
|
|
||||||
|
@progressBar.width('0%')
|
||||||
|
@progressText.text('0')
|
|
@ -8,7 +8,12 @@ class App.i18n
|
||||||
@translateDeep: (input, args...) ->
|
@translateDeep: (input, args...) ->
|
||||||
if _instance == undefined
|
if _instance == undefined
|
||||||
_instance ?= new _i18nSingleton()
|
_instance ?= new _i18nSingleton()
|
||||||
_instance.translateDeep(input, args)
|
_instance.translateDeep(input, args, false)
|
||||||
|
|
||||||
|
@translateDeepPlain: (input, args...) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _i18nSingleton()
|
||||||
|
_instance.translateDeep(input, args, true)
|
||||||
|
|
||||||
@translateContent: (string, args...) ->
|
@translateContent: (string, args...) ->
|
||||||
if _instance == undefined
|
if _instance == undefined
|
||||||
|
@ -230,17 +235,20 @@ class _i18nSingleton extends Spine.Module
|
||||||
return string if !string
|
return string if !string
|
||||||
@translate(string, args, true)
|
@translate(string, args, true)
|
||||||
|
|
||||||
translateDeep: (input, args) =>
|
translateDeep: (input, args, plain) =>
|
||||||
if _.isArray(input)
|
if _.isArray(input)
|
||||||
_.map input, (item) =>
|
_.map input, (item) =>
|
||||||
@translateDeep(item, args)
|
@translateDeep(item, args, plain)
|
||||||
else if _.isObject(input)
|
else if _.isObject(input)
|
||||||
_.reduce _.keys(input), (memo, item) =>
|
_.reduce _.keys(input), (memo, item) =>
|
||||||
memo[item] = @translateDeep(input[item])
|
memo[item] = @translateDeep(input[item], args, plain)
|
||||||
memo
|
memo
|
||||||
, {}
|
, {}
|
||||||
else
|
else
|
||||||
@translateInline(input, args)
|
if plain
|
||||||
|
@translatePlain(input, args)
|
||||||
|
else
|
||||||
|
@translateInline(input, args)
|
||||||
|
|
||||||
|
|
||||||
translateContent: (string, args) =>
|
translateContent: (string, args) =>
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
class _Singleton extends App._CollectionSingletonBase
|
|
||||||
event: 'ticket_create_attributes'
|
|
||||||
restEndpoint: '/ticket_create'
|
|
||||||
|
|
||||||
class App.TicketCreateCollection
|
|
||||||
_instance = new _Singleton
|
|
||||||
|
|
||||||
@get: ->
|
|
||||||
_instance.get()
|
|
||||||
|
|
||||||
@one: (callback, init = true) ->
|
|
||||||
_instance.bind(callback, init, true)
|
|
||||||
|
|
||||||
@bind: (callback, init = true) ->
|
|
||||||
_instance.bind(callback, init, false)
|
|
||||||
|
|
||||||
@unbind: (callback) ->
|
|
||||||
_instance.unbind(callback)
|
|
||||||
|
|
||||||
@unbindById: (id) ->
|
|
||||||
_instance.unbindById(id)
|
|
||||||
|
|
||||||
@trigger: ->
|
|
||||||
_instance.trigger()
|
|
||||||
|
|
||||||
@fetch: ->
|
|
||||||
_instance.fetch()
|
|
|
@ -1327,7 +1327,7 @@ class App.Utils
|
||||||
if type is 'email' && !e.attrs.value.match(/@/) || e.attrs.value.match(/\s/)
|
if type is 'email' && !e.attrs.value.match(/@/) || e.attrs.value.match(/\s/)
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
return false
|
return false
|
||||||
e.attrs.label = e.attrs.value
|
e.attrs.label ||= e.attrs.value
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
App.Delay.set(a, 500, undefined, 'tags')
|
App.Delay.set(a, 500, undefined, 'tags')
|
||||||
|
|
|
@ -654,6 +654,11 @@
|
||||||
var tokensBefore = this.getTokensList()
|
var tokensBefore = this.getTokensList()
|
||||||
this.setTokens( this.$input.val(), true )
|
this.setTokens( this.$input.val(), true )
|
||||||
|
|
||||||
|
// remove token text was cleared while editing
|
||||||
|
if (this.$input.data( 'edit' ) && !this.$input.val()) {
|
||||||
|
this.$element.val( this.getTokensList() )
|
||||||
|
}
|
||||||
|
|
||||||
if (tokensBefore == this.getTokensList() && this.$input.val().length)
|
if (tokensBefore == this.getTokensList() && this.$input.val().length)
|
||||||
return false // No tokens were added, do nothing (prevent form submit)
|
return false // No tokens were added, do nothing (prevent form submit)
|
||||||
|
|
||||||
|
|
|
@ -255,7 +255,7 @@
|
||||||
manager.ajaxUpload(manager.uploadsQueue.shift());
|
manager.ajaxUpload(manager.uploadsQueue.shift());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.abort = function (event) {
|
xhr.onabort = function (event) {
|
||||||
console.log('Upload abort');
|
console.log('Upload abort');
|
||||||
|
|
||||||
// Reduce number of active uploads:
|
// Reduce number of active uploads:
|
||||||
|
@ -269,6 +269,7 @@
|
||||||
// Triggered when upload fails:
|
// Triggered when upload fails:
|
||||||
xhr.onerror = function () {
|
xhr.onerror = function () {
|
||||||
console.log('Upload failed: ', upload.fileName);
|
console.log('Upload failed: ', upload.fileName);
|
||||||
|
upload.events.onError('Upload failed: ' + upload.fileName);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Append additional data if provided:
|
// Append additional data if provided:
|
||||||
|
|
|
@ -114,11 +114,22 @@
|
||||||
sel = window.getSelection()
|
sel = window.getSelection()
|
||||||
if (sel) {
|
if (sel) {
|
||||||
node = $(sel.anchorNode)
|
node = $(sel.anchorNode)
|
||||||
if (node && node.parent() && node.parent().is('blockquote')) {
|
|
||||||
e.preventDefault()
|
if (node.closest('blockquote').length > 0) {
|
||||||
document.execCommand('Insertparagraph')
|
|
||||||
document.execCommand('Outdent')
|
// Special handling when the line is not wrapped inside of a html element.
|
||||||
return
|
if (!node.is('div') && node.parent().is('blockquote') && node.text()) {
|
||||||
|
e.preventDefault()
|
||||||
|
document.execCommand('formatBlock', false, 'div')
|
||||||
|
document.execCommand('insertParagraph')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!e.shiftKey && node && (node.is('blockquote') || (node.parent() && node.parent().is('blockquote')) || !node.text())) {
|
||||||
|
e.preventDefault()
|
||||||
|
document.execCommand('insertParagraph')
|
||||||
|
document.execCommand('outdent')
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,17 +41,33 @@ App.ViewHelpers =
|
||||||
return '' if isNaN(parseInt(time))
|
return '' if isNaN(parseInt(time))
|
||||||
|
|
||||||
# Hours, minutes and seconds
|
# Hours, minutes and seconds
|
||||||
hrs = ~~parseInt((time / 3600))
|
hrs = ~~parseInt((time / 3600))
|
||||||
mins = ~~parseInt(((time % 3600) / 60))
|
mins = ~~parseInt(((time % 3600) / 60))
|
||||||
secs = parseInt(time % 60)
|
secs = parseInt(time % 60)
|
||||||
|
|
||||||
# Output like "1:01" or "4:03:59" or "123:03:59"
|
# Output like "1:01" or "4:03:59" or "123:03:59"
|
||||||
mins = "0#{mins}" if mins < 10
|
mins = "0#{mins}" if mins < 10
|
||||||
secs = "0#{secs}" if secs < 10
|
secs = "0#{secs}" if secs < 10
|
||||||
|
|
||||||
if hrs > 0
|
if hrs > 0
|
||||||
return "#{hrs}:#{mins}:#{secs}"
|
return "#{hrs}:#{mins}:#{secs}"
|
||||||
|
|
||||||
"#{mins}:#{secs}"
|
"#{mins}:#{secs}"
|
||||||
|
|
||||||
|
# define time_duration / hh:mm
|
||||||
|
time_duration_hh_mm: (time_in_minutes) ->
|
||||||
|
return '' if !time_in_minutes
|
||||||
|
return '' if isNaN(parseInt(time_in_minutes))
|
||||||
|
|
||||||
|
# Hours, minutes and seconds
|
||||||
|
hrs = ~~parseInt((time_in_minutes / 60))
|
||||||
|
mins = ~~parseInt((time_in_minutes % 60))
|
||||||
|
|
||||||
|
hrs = "0#{hrs}" if hrs < 10
|
||||||
|
mins = "0#{mins}" if mins < 10
|
||||||
|
|
||||||
|
"#{hrs}:#{mins}"
|
||||||
|
|
||||||
# define mask helper
|
# define mask helper
|
||||||
# mask an value like 'a***********yz'
|
# mask an value like 'a***********yz'
|
||||||
M: (item, start = 1, end = 2) ->
|
M: (item, start = 1, end = 2) ->
|
||||||
|
|
|
@ -234,12 +234,11 @@ class Singleton extends Base
|
||||||
|
|
||||||
failResponse: (options) =>
|
failResponse: (options) =>
|
||||||
(xhr, statusText, error, settings) =>
|
(xhr, statusText, error, settings) =>
|
||||||
if options.failResponseNoTrigger isnt true
|
switch settings.type
|
||||||
switch settings.type
|
when 'POST' then @createFailed()
|
||||||
when 'POST' then @createFailed()
|
when 'DELETE' then @destroyFailed()
|
||||||
when 'DELETE' then @destroyFailed()
|
# add errors to calllback
|
||||||
# add errors to calllback
|
@record.trigger('ajaxError', @record, xhr, statusText, error, settings)
|
||||||
@record.trigger('ajaxError', @record, xhr, statusText, error, settings)
|
|
||||||
|
|
||||||
#options.fail?.call(@record, settings)
|
#options.fail?.call(@record, settings)
|
||||||
detailsRaw = xhr.responseText
|
detailsRaw = xhr.responseText
|
||||||
|
@ -247,8 +246,7 @@ class Singleton extends Base
|
||||||
details = JSON.parse(detailsRaw)
|
details = JSON.parse(detailsRaw)
|
||||||
options.fail?.call(@record, settings, details)
|
options.fail?.call(@record, settings, details)
|
||||||
|
|
||||||
if options.failResponseNoTrigger isnt true
|
@record.trigger('destroy', @record)
|
||||||
@record.trigger('destroy', @record)
|
|
||||||
# /add errors to calllback
|
# /add errors to calllback
|
||||||
|
|
||||||
createFailed: ->
|
createFailed: ->
|
||||||
|
|
|
@ -916,20 +916,25 @@ set new attributes of model (remove already available attributes)
|
||||||
|
|
||||||
# use jquery instead of ._clone() because we need a deep copy of the obj
|
# use jquery instead of ._clone() because we need a deep copy of the obj
|
||||||
@org_configure_attributes = $.extend(true, [], @configure_attributes)
|
@org_configure_attributes = $.extend(true, [], @configure_attributes)
|
||||||
|
configure_attributes = $.extend(true, [], @configure_attributes)
|
||||||
|
allAttributes = []
|
||||||
for attribute in attributes
|
for attribute in attributes
|
||||||
@attributes.push attribute.name
|
@attributes.push attribute.name
|
||||||
|
|
||||||
found = false
|
found = false
|
||||||
for attribute_model, index in @configure_attributes
|
for attribute_model, index in configure_attributes
|
||||||
continue if attribute_model.name != attribute.name
|
continue if attribute_model.name != attribute.name
|
||||||
|
|
||||||
@configure_attributes[index] = _.extend(attribute_model, attribute)
|
allAttributes.push $.extend(true, attribute_model, attribute)
|
||||||
|
configure_attributes.splice(index, 1) # remove found attribute
|
||||||
|
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
|
|
||||||
if !found
|
if !found
|
||||||
@configure_attributes.push attribute
|
allAttributes.push $.extend(true, {}, attribute)
|
||||||
|
|
||||||
|
@configure_attributes = $.extend(true, [], allAttributes.concat(configure_attributes))
|
||||||
|
|
||||||
@resetAttributes: ->
|
@resetAttributes: ->
|
||||||
return if _.isEmpty(@org_configure_attributes)
|
return if _.isEmpty(@org_configure_attributes)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class App.KnowledgeBase extends App.Model
|
class App.KnowledgeBase extends App.Model
|
||||||
@configure 'KnowledgeBase', 'iconset', 'color_highlight', 'color_header', 'translation_ids', 'locale_ids', 'homepage_layout', 'category_layout', 'custom_address'
|
@configure 'KnowledgeBase', 'iconset', 'color_highlight', 'color_header', 'color_header_link', 'translation_ids', 'locale_ids', 'homepage_layout', 'category_layout', 'custom_address'
|
||||||
@extend Spine.Model.Ajax
|
@extend Spine.Model.Ajax
|
||||||
@extend App.KnowledgeBaseActions
|
@extend App.KnowledgeBaseActions
|
||||||
@url: @apiPath + '/knowledge_bases'
|
@url: @apiPath + '/knowledge_bases'
|
||||||
|
@ -148,6 +148,17 @@ class App.KnowledgeBase extends App.Model
|
||||||
display: false
|
display: false
|
||||||
horizontal: true
|
horizontal: true
|
||||||
shown: true
|
shown: true
|
||||||
|
}, {
|
||||||
|
name: 'color_header_link'
|
||||||
|
display: 'Header Link Color'
|
||||||
|
tag: 'color'
|
||||||
|
style: 'block'
|
||||||
|
null: false
|
||||||
|
screen:
|
||||||
|
admin_style_color_header_link:
|
||||||
|
display: false
|
||||||
|
horizontal: true
|
||||||
|
shown: true
|
||||||
# Layout picker is disabled in V1
|
# Layout picker is disabled in V1
|
||||||
#}, {
|
#}, {
|
||||||
# name: 'homepage_layout'
|
# name: 'homepage_layout'
|
||||||
|
|
|
@ -6,8 +6,8 @@ class App.ObjectManagerAttribute extends App.Model
|
||||||
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
|
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
|
||||||
{ name: 'display', display: 'Display', tag: 'input', type: 'text', limit: 100, null: false },
|
{ name: 'display', display: 'Display', tag: 'input', type: 'text', limit: 100, null: false },
|
||||||
{ name: 'object', display: 'Object', tag: 'input', readonly: 1 },
|
{ name: 'object', display: 'Object', tag: 'input', readonly: 1 },
|
||||||
{ name: 'position', display: 'Position', tag: 'input', readonly: 1 },
|
|
||||||
{ name: 'active', display: 'Active', tag: 'active', default: true },
|
{ name: 'active', display: 'Active', tag: 'active', default: true },
|
||||||
{ name: 'data_type', display: 'Format', tag: 'object_manager_attribute', null: false },
|
{ name: 'data_type', display: 'Format', tag: 'object_manager_attribute', null: false },
|
||||||
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
|
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
|
||||||
|
{ name: 'position', display: 'Position', tag: 'integer', type: 'number', limit: 100, null: true },
|
||||||
]
|
]
|
||||||
|
|
|
@ -36,7 +36,7 @@ Using **Organisations** you can **group** customers. This has among others two i
|
||||||
userResult = ->
|
userResult = ->
|
||||||
users = []
|
users = []
|
||||||
for user_id in member_ids
|
for user_id in member_ids
|
||||||
user = App.User.find(user_id)
|
user = App.User.fullLocal(user_id)
|
||||||
continue if !user
|
continue if !user
|
||||||
users.push(user)
|
users.push(user)
|
||||||
return users
|
return users
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class App.Sla extends App.Model
|
class App.Sla extends App.Model
|
||||||
@configure 'Sla', 'name', 'first_response_time', 'update_time', 'solution_time', 'condition', 'calendar_id'
|
@configure 'Sla', 'name', 'first_response_time', 'response_time', 'update_time', 'solution_time', 'condition', 'calendar_id'
|
||||||
@extend Spine.Model.Ajax
|
@extend Spine.Model.Ajax
|
||||||
@url: @apiPath + '/slas'
|
@url: @apiPath + '/slas'
|
||||||
@configure_attributes = [
|
@configure_attributes = [
|
||||||
|
@ -12,6 +12,7 @@ class App.Sla extends App.Model
|
||||||
{ name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 },
|
{ name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 },
|
||||||
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
|
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
|
||||||
{ name: 'first_response_time',skipRendering: true },
|
{ name: 'first_response_time',skipRendering: true },
|
||||||
|
{ name: 'response_time', skipRendering: true },
|
||||||
{ name: 'update_time', skipRendering: true },
|
{ name: 'update_time', skipRendering: true },
|
||||||
{ name: 'solution_time', skipRendering: true },
|
{ name: 'solution_time', skipRendering: true },
|
||||||
]
|
]
|
||||||
|
|
|
@ -344,9 +344,12 @@ class App.User extends App.Model
|
||||||
@sameOrganization?(requester)
|
@sameOrganization?(requester)
|
||||||
|
|
||||||
isChangeableBy: (requester) ->
|
isChangeableBy: (requester) ->
|
||||||
|
# full access for admins
|
||||||
return true if requester.permission('admin.user')
|
return true if requester.permission('admin.user')
|
||||||
# allow agents to change customers
|
# forbid non-agents to change users
|
||||||
return false if !requester.permission('ticket.agent')
|
return false if !requester.permission('ticket.agent')
|
||||||
|
# allow agents to change customers only
|
||||||
|
return false if @permission(['admin.user', 'ticket.agent'])
|
||||||
@permission('ticket.customer')
|
@permission('ticket.customer')
|
||||||
|
|
||||||
isDeleteableBy: (requester) ->
|
isDeleteableBy: (requester) ->
|
||||||
|
|
|
@ -6,11 +6,15 @@
|
||||||
<% if @has_any_tickets: %>
|
<% if @has_any_tickets: %>
|
||||||
<p><%- @T('You have no tickets to display in this overview.') %></p>
|
<p><%- @T('You have no tickets to display in this overview.') %></p>
|
||||||
<% else: %>
|
<% else: %>
|
||||||
<p><%- @T('You have not created a ticket yet.') %></p>
|
<% if @is_allowed_to_create_ticket: %>
|
||||||
<p><%- @T('The way to communicate with us is this thing called "ticket".') %></p>
|
<p><%- @T('You have not created a ticket yet.') %></p>
|
||||||
<p><%- @T('Please click the button below to create your first one.') %></p>
|
<p><%- @T('The way to communicate with us is this thing called "ticket".') %></p>
|
||||||
|
<p><%- @T('Please click the button below to create your first one.') %></p>
|
||||||
|
|
||||||
<p><a class="btn btn--primary" href="#customer_ticket_new"><%- @T('Create your first ticket') %></a></p>
|
<p><a class="btn btn--primary" href="#customer_ticket_new"><%- @T('Create your first ticket') %></a></p>
|
||||||
|
<% else: %>
|
||||||
|
<p><%- @T('You currently don\'t have any tickets.') %></p>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group js-sure">
|
<div class="form-group js-sure">
|
||||||
<h3 class="danger-color"><%- @T('Warning') %></h3>
|
<h3 class="danger-color"><%- @T('Warning') %></h3>
|
||||||
<p class="danger-color"><%- @T('There is no rollback of this deletion possible. If you are absolutely sure to do this, then type in "%s" into the input.', App.i18n.translateInline('delete').toUpperCase()) %></p>
|
<p class="danger-color"><%- @T('There is no rollback of this deletion possible. If you are absolutely sure to do this, then type in "%s" into the input.', App.i18n.translatePlain('delete').toUpperCase()) %></p>
|
||||||
<%- @sure_html %>
|
<%- @sure_html %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<%- @T('Uploading') %> (<span class="js-percentage">0</span>%) ...
|
<%- @T('Uploading') %> (<span class="js-percentage">0</span>%) ...
|
||||||
</div>
|
</div>
|
||||||
<div class="attachmentUpload-cancel js-cancel">
|
<div class="attachmentUpload-cancel js-cancel">
|
||||||
<%- @Icon('diagonal-cross') %></div><%- @T('Cancel Upload') %>
|
<%- @Icon('diagonal-cross') %><%- @T('Cancel Upload') %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="attachmentUpload-progressBar" style="width: 0%"></div>
|
<div class="attachmentUpload-progressBar" style="width: 0%"></div>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<input type="text" disabled="disabled" readonly="readonly" value="<%= @attribute.value %>" class="form-control" />
|
|
@ -20,8 +20,8 @@
|
||||||
<td data-day="<%- id %>" data-slot="<%- slot %>" class="form-group day-time<%- ' is-active' if @hours[id].active %>">
|
<td data-day="<%- id %>" data-slot="<%- slot %>" class="form-group day-time<%- ' is-active' if @hours[id].active %>">
|
||||||
<label for="<%- id %>_start_time">from</label>
|
<label for="<%- id %>_start_time">from</label>
|
||||||
<input type="text" id="<%- id %>_start_time" name="<%= @attribute.name %>::<%- id %>::start" value="<%- @hours[id].timeframes[slot][0] %>" data-day="<%- id %>" data-slot="<%- slot %>" data-i="0" class="form-control form-control--small time js-time">
|
<input type="text" id="<%- id %>_start_time" name="<%= @attribute.name %>::<%- id %>::start" value="<%- @hours[id].timeframes[slot][0] %>" data-day="<%- id %>" data-slot="<%- slot %>" data-i="0" class="form-control form-control--small time js-time">
|
||||||
<label for="monday_end_time">till</label>
|
<label for="<%- id %>_end_time">till</label>
|
||||||
<input type="text" id="monday_end_time" name="<%= @attribute.name %>::<%- id %>::end" value="<%- @hours[id].timeframes[slot][1] %>" data-day="<%- id %>" data-slot="<%- slot %>" data-i="1" class="form-control form-control--small time js-time">
|
<input type="text" id="<%- id %>_end_time" name="<%= @attribute.name %>::<%- id %>::end" value="<%- @hours[id].timeframes[slot][1] %>" data-day="<%- id %>" data-slot="<%- slot %>" data-i="1" class="form-control form-control--small time js-time">
|
||||||
<% else: %>
|
<% else: %>
|
||||||
<td class="empty-cell">
|
<td class="empty-cell">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<% if @attribute.multiple: %>
|
<% if @attribute.multiple: %>
|
||||||
<%- @tokens %>
|
<%- @tokens %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<input name="<%- @attribute.name %>_completion" class="user-select token-input js-objectSelect" autocapitalize="off" placeholder="<%- @Ti(@attribute.placeholder) %>" autocomplete="off" <%= @attribute.autofocus %> role="textbox" aria-autocomplete="list" value="<%= @name %>" aria-haspopup="true">
|
<input name="<%= @attribute.name %>_completion" class="user-select token-input js-objectSelect form-control" autocapitalize="off" placeholder="<%- @Ti(@attribute.placeholder) %>" autocomplete="off" <%= @attribute.autofocus %> role="textbox" aria-autocomplete="list" value="<%= @name %>" aria-haspopup="true">
|
||||||
<% if @attribute.disableCreateObject isnt true: %><%- @Icon('arrow-down', 'dropdown-arrow') %><% end %>
|
<% if @attribute.disableCreateObject isnt true: %><%- @Icon('arrow-down', 'dropdown-arrow') %><% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,9 @@
|
||||||
<%- @Icon('arrow-left') %>
|
<%- @Icon('arrow-left') %>
|
||||||
<span class="btn-label"><%- @T('Back') %></span>
|
<span class="btn-label"><%- @T('Back') %></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<li class="recipientList-controls js-showMoreMembers" organization-member-limit="10">
|
||||||
|
<div class="btn btn--action btn--onDark">
|
||||||
|
<span class="btn-label"><%- @T('show more') %></span>
|
||||||
|
</div>
|
||||||
</ul>
|
</ul>
|
|
@ -6,7 +6,7 @@
|
||||||
<th><%- @T('Time') %> <span class="text-muted"><%- @T('in hours') %></span>
|
<th><%- @T('Time') %> <span class="text-muted"><%- @T('in hours') %></span>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="form-group">
|
<tr>
|
||||||
<td class="u-positionOrigin">
|
<td class="u-positionOrigin">
|
||||||
<label class="checkbox-replacement checkbox-replacement--fullscreen dont-grey-out">
|
<label class="checkbox-replacement checkbox-replacement--fullscreen dont-grey-out">
|
||||||
<input type="checkbox" class="js-activateRow" id="first_response_time" name="first_response_time_enabled">
|
<input type="checkbox" class="js-activateRow" id="first_response_time" name="first_response_time_enabled">
|
||||||
|
@ -16,25 +16,51 @@
|
||||||
<td class="u-clickable js-forward-click">
|
<td class="u-clickable js-forward-click">
|
||||||
<div><%- @T('First Response Time') %></div>
|
<div><%- @T('First Response Time') %></div>
|
||||||
<p class="subtle"><%- @T('Timeframe for the first response.') %></p>
|
<p class="subtle"><%- @T('Timeframe for the first response.') %></p>
|
||||||
<td class="u-clickable js-focus-input">
|
<td class="u-clickable js-focus-input form-group">
|
||||||
<input type="hidden" name="first_response_time" value="<%= @first_response_time %>" class="js-timeConvertTo">
|
<input type="hidden" name="first_response_time" value="<%= @first_response_time %>" class="js-timeConvertTo">
|
||||||
<input type="text" value="<%= @first_response_time_in_text %>" class="form-control form-control--small timeframe js-timeConvertFrom" placeholder="hh:mm" name="first_response_time_in_text" data-name="first_response_time">
|
<input type="text" value="<%= @first_response_time_in_text %>" class="form-control form-control--small timeframe js-timeConvertFrom" placeholder="hh:mm" name="first_response_time_in_text" data-name="first_response_time">
|
||||||
|
|
||||||
<tr class="form-group">
|
<tr>
|
||||||
<td class="u-positionOrigin">
|
<td class="u-positionOrigin">
|
||||||
<label class="checkbox-replacement checkbox-replacement--fullscreen dont-grey-out">
|
<label class="checkbox-replacement checkbox-replacement--fullscreen dont-grey-out">
|
||||||
<input type="checkbox" class="js-activateRow" id="update_time" name="update_time_enabled">
|
<input type="checkbox" class="js-activateRow" id="update_time" name="update_time_enabled">
|
||||||
<%- @Icon('checkbox', 'icon-unchecked') %>
|
<%- @Icon('checkbox', 'icon-unchecked') %>
|
||||||
<%- @Icon('checkbox-checked', 'icon-checked') %>
|
<%- @Icon('checkbox-checked', 'icon-checked') %>
|
||||||
</label>
|
</label>
|
||||||
<td class="u-clickable js-forward-click">
|
|
||||||
<div><%- @T('Update Time') %></div>
|
|
||||||
<p class="subtle"><%- @T('Timeframe for every following response.') %></p>
|
|
||||||
<td>
|
<td>
|
||||||
<input type="hidden" name="update_time" value="<%= @update_time %>" class="js-timeConvertTo">
|
<div class="u-clickable js-forward-click">
|
||||||
<input type="text" value="<%= @update_time_in_text %>" class="form-control form-control--small timeframe js-timeConvertFrom" placeholder="hh:mm" name="update_time_in_text" data-name="update_time">
|
<div><%- @T('Update Time') %></div>
|
||||||
|
<p class="subtle"><%- @T('Timeframe between agent updates or for an agent to respond.') %></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<tr class="form-group">
|
<p class="sla_radio_container js-forward-radio">
|
||||||
|
<label class="inline-label radio-replacement">
|
||||||
|
<input class="js-updateTypeSelector" type="radio" name="update_type" value="update" <% if @update_time: %>checked<% end %>>
|
||||||
|
<%- @Icon('radio', 'icon-unchecked') %>
|
||||||
|
<%- @Icon('radio-checked', 'icon-checked') %>
|
||||||
|
<span class="label-text"><%- @T('between agent updates') %></span>
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="sla_radio_container js-forward-radio u-clickable">
|
||||||
|
<label class="inline-label radio-replacement">
|
||||||
|
<input class="js-updateTypeSelector" type="radio" name="update_type" value="response" <% if @response_time: %>checked<% end %>>
|
||||||
|
<%- @Icon('radio', 'icon-unchecked') %>
|
||||||
|
<%- @Icon('radio-checked', 'icon-checked') %>
|
||||||
|
<span class="label-text"><%- @T('for an agent to respond') %></span>
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<td class="form-group u-clickable js-focus-input u-clickable">
|
||||||
|
<span class="form-group">
|
||||||
|
<input type="hidden" name="update_time" value="<%= @update_time %>" class="js-timeConvertTo">
|
||||||
|
<input type="text" value="<%= @update_time_in_text %>" class="form-control form-control--small timeframe js-timeConvertFrom <% if @response_time: %>hidden<% end %>" placeholder="hh:mm" name="update_time_in_text" data-name="update_time">
|
||||||
|
</span>
|
||||||
|
<span class="form-group">
|
||||||
|
<input type="hidden" name="response_time" value="<%= @response_time %>" class="js-timeConvertTo">
|
||||||
|
<input type="text" value="<%= @response_time_in_text %>" class="form-control form-control--small timeframe js-timeConvertFrom <% if !@response_time: %>hidden<% end %>" placeholder="hh:mm" name="response_time_in_text" data-name="response_time">
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<tr>
|
||||||
<td class="u-positionOrigin">
|
<td class="u-positionOrigin">
|
||||||
<label class="checkbox-replacement checkbox-replacement--fullscreen dont-grey-out">
|
<label class="checkbox-replacement checkbox-replacement--fullscreen dont-grey-out">
|
||||||
<input type="checkbox" id="solution_time" class="js-activateRow" name="solution_time_enabled">
|
<input type="checkbox" id="solution_time" class="js-activateRow" name="solution_time_enabled">
|
||||||
|
@ -44,7 +70,7 @@
|
||||||
<td class="u-clickable js-forward-click">
|
<td class="u-clickable js-forward-click">
|
||||||
<div><%- @T('Solution Time') %></div>
|
<div><%- @T('Solution Time') %></div>
|
||||||
<p class="subtle"><%- @T('Timeframe for solving the problem.') %></p>
|
<p class="subtle"><%- @T('Timeframe for solving the problem.') %></p>
|
||||||
<td>
|
<td class="form-group u-clickable js-focus-input">
|
||||||
<input type="hidden" name="solution_time" value="<%= @solution_time %>" class="js-timeConvertTo">
|
<input type="hidden" name="solution_time" value="<%= @solution_time %>" class="js-timeConvertTo">
|
||||||
<input type="text" value="<%= @solution_time_in_text %>" class="form-control form-control--small timeframe js-timeConvertFrom" placeholder="hh:mm" name="solution_time_in_text" data-name="solution_time">
|
<input type="text" value="<%= @solution_time_in_text %>" class="form-control form-control--small timeframe js-timeConvertFrom" placeholder="hh:mm" name="solution_time_in_text" data-name="solution_time">
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
<table class="settings-list" style="width: 100%;">
|
<table class="settings-list" style="width: 100%;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="50%"><%- @T('Name') %>
|
<th width="50%"><%- @T('Value') %>
|
||||||
<th width="50%"><%- @T('Description') %>
|
<th width="50%"><%- @T('Description') %>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -91,24 +91,30 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
<p><%- @T('Default caller id.') %>
|
<p><%- @T('Settings') %>
|
||||||
|
|
||||||
<div class="settings-entry">
|
<div class="settings-entry">
|
||||||
<table class="settings-list" style="width: 100%;">
|
<table class="settings-list" style="width: 100%;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="50%"><%- @T('Default caller id') %>
|
<th width="50%"><%- @T('Value') %>
|
||||||
<th width="50%"><%- @T('Note') %>
|
<th width="50%"><%- @T('Description') %>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<!--
|
||||||
<tr>
|
<tr>
|
||||||
<td class="settings-list-control-cell"><input name="default_caller_id" value="<%= @config.outbound.default_caller_id %>" placeholder="4930609854189" class="form-control form-control--small js-summary">
|
<td class="settings-list-control-cell"><input name="default_caller_id" value="<%= @config.outbound.default_caller_id %>" placeholder="4930609854189" class="form-control form-control--small js-summary">
|
||||||
<td class="settings-list-row-control"><%- @T('Default caller id for outbound calls.') %>
|
<td class="settings-list-row-control"><%- @T('Default caller id for outbound calls.') %>
|
||||||
|
-->
|
||||||
|
<tr>
|
||||||
|
<td class="settings-list-control-cell js-viewLimit">
|
||||||
|
<td class="settings-list-row-control"><%- @T('Shown records in caller log.') %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
<h2><%- @T('User assignment to telephones') %></h2>
|
<h2><%- @T('User assignment to telephones') %></h2>
|
||||||
|
|
||||||
<p><%- @T('User assignment to telephones to be able to offer extended like open new ticket screen on answering a call.') %>
|
<p><%- @T('User assignment to telephones to be able to offer extended like open new ticket screen on answering a call.') %>
|
||||||
|
|
|
@ -79,38 +79,22 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p><%- @T('Default caller id.') %>
|
<p><%- @T('Settings') %>
|
||||||
|
|
||||||
<div class="settings-entry">
|
<div class="settings-entry">
|
||||||
<table class="settings-list" style="width: 100%;">
|
<table class="settings-list" style="width: 100%;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="50%"><%- @T('Default caller id') %>
|
<th width="50%"><%- @T('Value') %>
|
||||||
<th width="50%"><%- @T('Note') %>
|
<th width="50%"><%- @T('Description') %>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="settings-list-control-cell"><input name="default_caller_id" value="<%= @config.outbound.default_caller_id %>" placeholder="4930609854189" class="form-control form-control--small js-summary">
|
<td class="settings-list-control-cell"><input name="default_caller_id" value="<%= @config.outbound.default_caller_id %>" placeholder="4930609854189" class="form-control form-control--small js-summary">
|
||||||
<td class="settings-list-row-control"><%- @T('Default caller id for outbound calls.') %>
|
<td class="settings-list-row-control"><%- @T('Default caller id for outbound calls.') %>
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p><%- @T('In order for Zammad to access %s, a %s API user and password must be stored here', 'Sipgate', 'Sipgate') %>:<p>
|
|
||||||
<div class="settings-entry">
|
|
||||||
<table class="settings-list" style="width: 100%;">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th width="20%"><%- @T('Type') %>
|
<td class="settings-list-control-cell js-viewLimit">
|
||||||
<th width="80%"><%- @T('Content') %>
|
<td class="settings-list-row-control"><%- @T('Shown records in caller log.') %>
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="settings-list-row-control"><%- @T('API User') %>
|
|
||||||
<td class="settings-list-control-cell"><input type="input" class="form-control form-control--small js-select" value="<%= @config.api_user %>" name="api_user" placeholder="someuser@example.com">
|
|
||||||
<tr>
|
|
||||||
<td class="settings-list-row-control"><%- @T('API Password') %>
|
|
||||||
<td class="settings-list-control-cell"><input type="password" class="form-control form-control--small js-select" value="<%= @config.api_password %>" name="api_password" placeholder="">
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -26,11 +26,12 @@ class App.KnowledgeBaseNewModal extends App.ControllerModal
|
||||||
App.UiElement[attribute.tag].prepareParams?(attribute, dom, params)
|
App.UiElement[attribute.tag].prepareParams?(attribute, dom, params)
|
||||||
|
|
||||||
applyDefaults: (params) ->
|
applyDefaults: (params) ->
|
||||||
params['iconset'] = 'FontAwesome'
|
params['iconset'] = 'FontAwesome'
|
||||||
params['color_highlight'] = '#38ae6a'
|
params['color_highlight'] = '#38ae6a'
|
||||||
params['color_header'] = '#f9fafb'
|
params['color_header'] = '#f9fafb'
|
||||||
params['homepage_layout'] = 'grid'
|
params['color_header_link'] = 'hsl(206,8%,50%)'
|
||||||
params['category_layout'] = 'grid'
|
params['homepage_layout'] = 'grid'
|
||||||
|
params['category_layout'] = 'grid'
|
||||||
|
|
||||||
onSubmit: (e) ->
|
onSubmit: (e) ->
|
||||||
params = @formParams(@el)
|
params = @formParams(@el)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<div class="kb-menu-preview">
|
<div class="kb-menu-preview">
|
||||||
<div class="label"><%= kb_locale.systemLocale().name %></div>
|
<div class="label"><%= kb_locale.systemLocale().name %></div>
|
||||||
|
|
||||||
<div class="kb-menu-preview-container kb-menu-preview-container--<%= location.identifier %>" style="background-color: <%= location.color %>">
|
<div class="kb-menu-preview-container kb-menu-preview-container--<%= location.identifier %>" style="background-color: <%= location.color %>; color: <%= location.color_link %>;">
|
||||||
<% menu_items = App.KnowledgeBaseMenuItem.using_kb_locale_location(kb_locale, location.identifier) %>
|
<% menu_items = App.KnowledgeBaseMenuItem.using_kb_locale_location(kb_locale, location.identifier) %>
|
||||||
|
|
||||||
<% if menu_items.length == 0: %>
|
<% if menu_items.length == 0: %>
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
<th class=""><%- @T('Display') %></th>
|
<th class=""><%- @T('Display') %></th>
|
||||||
<th class=""><%- @T('Name') %></th>
|
<th class=""><%- @T('Name') %></th>
|
||||||
<th class="" style="width: 200px;"><%- @T('Type') %></th>
|
<th class="" style="width: 200px;"><%- @T('Type') %></th>
|
||||||
|
<th><%- @T('Position') %></th>
|
||||||
<th class="" style="width: 140px;"><%- @T('Action') %></th>
|
<th class="" style="width: 140px;"><%- @T('Action') %></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -64,6 +65,7 @@
|
||||||
<td><%= item.display %></td>
|
<td><%= item.display %></td>
|
||||||
<td><%= item.name %></td>
|
<td><%= item.name %></td>
|
||||||
<td><%= item.data_type %></td>
|
<td><%= item.data_type %></td>
|
||||||
|
<td><%= item.position %></td>
|
||||||
<td>
|
<td>
|
||||||
<% if item.to_create is true: %>
|
<% if item.to_create is true: %>
|
||||||
<%- @T('will be created') %>
|
<%- @T('will be created') %>
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
|
<p>
|
||||||
|
<%- @T('The installation of packages comes with security implications, because arbitrary code will be executed in the context of the Zammad application.') %>
|
||||||
|
<br>
|
||||||
|
<%- @T('Only packages from known, trusted and verfied sources should be installed.') %>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<%- @T('After installing, updating or uninstalling packages the following commands need to be executed on the server:') %>
|
<%- @T('After installing, updating or uninstalling packages the following commands need to be executed on the server:') %>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<% if @object.member_ids: %>
|
<% if @object.member_ids: %>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="popover-block">
|
<div class="popover-block">
|
||||||
<label><%- @T('Members') %></label>
|
<label><%- @T('Members') %> (<%= @object.member_ids.length %>)</label>
|
||||||
<div class="userList js-userList"></div>
|
<div class="userList js-userList"></div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -34,15 +34,19 @@
|
||||||
<div class="action-block action-block--flex">
|
<div class="action-block action-block--flex">
|
||||||
<div class="label"><%- @T('Escalation Times') %></div>
|
<div class="label"><%- @T('Escalation Times') %></div>
|
||||||
<% if sla.first_response_time: %>
|
<% if sla.first_response_time: %>
|
||||||
<%- sla.first_response_time_in_text %> <%- @T('hours') %> - <%- @T('First Response Time') %>
|
<%- @time_duration_hh_mm(sla.first_response_time) %> <%- @T('hours') %> - <%- @T('First Response Time') %>
|
||||||
|
<% end %>
|
||||||
|
<% if sla.response_time: %>
|
||||||
|
<br>
|
||||||
|
<%- @time_duration_hh_mm(sla.response_time) %> <%- @T('hours') %> - <%- @T('Response Time') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if sla.update_time: %>
|
<% if sla.update_time: %>
|
||||||
<br>
|
<br>
|
||||||
<%- sla.update_time_in_text %> <%- @T('hours') %> - <%- @T('Update Time') %>
|
<%- @time_duration_hh_mm(sla.update_time) %> <%- @T('hours') %> - <%- @T('Update Time') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if sla.solution_time: %>
|
<% if sla.solution_time: %>
|
||||||
<br>
|
<br>
|
||||||
<%- sla.solution_time_in_text %> <%- @T('hours') %> - <%- @T('Solution Time') %>
|
<%- @time_duration_hh_mm(sla.solution_time) %> <%- @T('hours') %> - <%- @T('Solution Time') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -65,9 +65,9 @@ Date.prototype.getWeek = function() {
|
||||||
|
|
||||||
function difference(object1, object2) {
|
function difference(object1, object2) {
|
||||||
var changes = {};
|
var changes = {};
|
||||||
for (var name in object1) {
|
_.uniq(Object.keys(object1).concat(Object.keys(object2))).forEach(function(name) {
|
||||||
if (name in object2) {
|
if (name in object1 && name in object2) {
|
||||||
if (_.isObject(object2[name]) && !_.isArray(object2[name])) {
|
if (_.isObject(object1[name]) && !_.isArray(object1[name]) && _.isObject(object2[name]) && !_.isArray(object2[name])) {
|
||||||
var diff = difference(object1[name], object2[name]);
|
var diff = difference(object1[name], object2[name]);
|
||||||
if (!_.isEmpty(diff)) {
|
if (!_.isEmpty(diff)) {
|
||||||
changes[name] = diff;
|
changes[name] = diff;
|
||||||
|
@ -75,8 +75,10 @@ function difference(object1, object2) {
|
||||||
} else if (!_.isEqual(object1[name], object2[name])) {
|
} else if (!_.isEqual(object1[name], object2[name])) {
|
||||||
changes[name] = object2[name];
|
changes[name] = object2[name];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
changes[name] = object2[name]
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,39 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Fira Sans';
|
font-family: 'Fira Sans';
|
||||||
src: url('fonts/FiraSans-Bold.eot');
|
src: url('assets/fonts/FiraSans-Bold.eot');
|
||||||
src: url('fonts/FiraSans-Bold.woff2') format('woff2'),
|
src: url('assets/fonts/FiraSans-Bold.woff2') format('woff2'),
|
||||||
url('fonts/FiraSans-Bold.woff') format('woff'),
|
url('assets/fonts/FiraSans-Bold.woff') format('woff'),
|
||||||
url('fonts/FiraSans-Bold.ttf') format('truetype');
|
url('assets/fonts/FiraSans-Bold.ttf') format('truetype');
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Fira Sans';
|
font-family: 'Fira Sans';
|
||||||
src: url('fonts/FiraSans-Regular.eot');
|
src: url('assets/fonts/FiraSans-Regular.eot');
|
||||||
src: url('fonts/FiraSans-Regular.woff2') format('woff2'),
|
src: url('assets/fonts/FiraSans-Regular.woff2') format('woff2'),
|
||||||
url('fonts/FiraSans-Regular.woff') format('woff'),
|
url('assets/fonts/FiraSans-Regular.woff') format('woff'),
|
||||||
url('fonts/FiraSans-Regular.ttf') format('truetype');
|
url('assets/fonts/FiraSans-Regular.ttf') format('truetype');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Fira Sans';
|
font-family: 'Fira Sans';
|
||||||
src: url('fonts/FiraSans-Medium.eot');
|
src: url('assets/fonts/FiraSans-Medium.eot');
|
||||||
src: url('fonts/FiraSans-Medium.woff2') format('woff2'),
|
src: url('assets/fonts/FiraSans-Medium.woff2') format('woff2'),
|
||||||
url('fonts/FiraSans-Medium.woff') format('woff'),
|
url('assets/fonts/FiraSans-Medium.woff') format('woff'),
|
||||||
url('fonts/FiraSans-Medium.ttf') format('truetype');
|
url('assets/fonts/FiraSans-Medium.ttf') format('truetype');
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Fira Sans';
|
font-family: 'Fira Sans';
|
||||||
src: url('fonts/FiraSans-Light.eot');
|
src: url('assets/fonts/FiraSans-Light.eot');
|
||||||
src: url('fonts/FiraSans-Light.woff2') format('woff2'),
|
src: url('assets/fonts/FiraSans-Light.woff2') format('woff2'),
|
||||||
url('fonts/FiraSans-Light.woff') format('woff'),
|
url('assets/fonts/FiraSans-Light.woff') format('woff'),
|
||||||
url('fonts/FiraSans-Light.ttf') format('truetype');
|
url('assets/fonts/FiraSans-Light.ttf') format('truetype');
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
|
@ -358,6 +358,26 @@ ol, ul {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: hsla(0, 0%, 0%, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
box-decoration-break: clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
|
.hljs {
|
||||||
|
padding: 2px 4px;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code.hljs {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 9.5px;
|
padding: 9.5px;
|
||||||
|
@ -371,30 +391,26 @@ pre {
|
||||||
border: 1px solid hsl(0,0%,90%);
|
border: 1px solid hsl(0,0%,90%);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-content pre {
|
||||||
|
background: hsl(0, 0%, 97%);
|
||||||
|
border: 1px solid hsl(0, 0%, 87%);
|
||||||
|
}
|
||||||
|
|
||||||
pre code {
|
pre code {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
background-color: transparent;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs,
|
|
||||||
code {
|
|
||||||
background: none;
|
background: none;
|
||||||
padding: 2px 4px;
|
border-radius: 0;
|
||||||
font-size: 0.88em;
|
border: none;
|
||||||
}
|
overflow-x: auto;
|
||||||
|
|
||||||
code:not(.hljs) {
|
&.hljs {
|
||||||
border: 1px solid rgba(0,0,0,.2);
|
padding: 0;
|
||||||
border-radius: 3px;
|
background: none;
|
||||||
white-space: nowrap;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pre code.hljs {
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea::placeholder,
|
.textarea::placeholder,
|
||||||
|
@ -2661,7 +2677,7 @@ input.has-error {
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: hsl(206,8%,50%);
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
|
@ -4997,6 +5013,11 @@ footer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--vacation {
|
||||||
|
filter: grayscale(70%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
&--idle {
|
&--idle {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
@ -5007,11 +5028,6 @@ footer {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--vacation {
|
|
||||||
filter: grayscale(70%);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--unique {
|
&--unique {
|
||||||
background-image: image_url("/assets/images/avatar-bg.png");
|
background-image: image_url("/assets/images/avatar-bg.png");
|
||||||
background-size: 300px 226px;
|
background-size: 300px 226px;
|
||||||
|
@ -7051,7 +7067,7 @@ footer {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
color: #b3b3b3;
|
color: #b3b3b3;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@extend .u-unclickable, .u-textTruncate;
|
@extend .u-textTruncate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.attachments:not(:empty) {
|
.attachments:not(:empty) {
|
||||||
|
@ -7060,6 +7076,16 @@ footer {
|
||||||
margin: 6px -12px 30px;
|
margin: 6px -12px 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ticket-create .attachments:not(:empty) {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-create .attachment--row {
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
.attachment.attachment--row {
|
.attachment.attachment--row {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 1px 10px 1px 7px;
|
padding: 1px 10px 1px 7px;
|
||||||
|
@ -8442,6 +8468,10 @@ footer {
|
||||||
|
|
||||||
.dropdown li.with-category, .dropdown.dropdown--actions li.with-category {
|
.dropdown li.with-category, .dropdown.dropdown--actions li.with-category {
|
||||||
line-height: 19.5px;
|
line-height: 19.5px;
|
||||||
|
|
||||||
|
small {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown.dropdown--actions li.with-category {
|
.dropdown.dropdown--actions li.with-category {
|
||||||
|
@ -13143,3 +13173,10 @@ span.is-disabled {
|
||||||
.text-modules-box {
|
.text-modules-box {
|
||||||
max-height: 40vh;
|
max-height: 40vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sla_times {
|
||||||
|
.sla_radio_container {
|
||||||
|
padding-top: 0.5em;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ class ApplicationController < ActionController::Base
|
||||||
include ApplicationController::RendersModels
|
include ApplicationController::RendersModels
|
||||||
include ApplicationController::HasUser
|
include ApplicationController::HasUser
|
||||||
include ApplicationController::HasResponseExtentions
|
include ApplicationController::HasResponseExtentions
|
||||||
|
include ApplicationController::HasDownload
|
||||||
include ApplicationController::PreventsCsrf
|
include ApplicationController::PreventsCsrf
|
||||||
include ApplicationController::HasSecureContentSecurityPolicyForDownloads
|
|
||||||
include ApplicationController::LogsHttpAccess
|
include ApplicationController::LogsHttpAccess
|
||||||
include ApplicationController::Authorizes
|
include ApplicationController::Authorizes
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,6 +43,10 @@ module ApplicationController::Authenticates
|
||||||
end
|
end
|
||||||
|
|
||||||
def authentication_check_only(auth_param = {})
|
def authentication_check_only(auth_param = {})
|
||||||
|
if Rails.env.test? && ENV['FAKE_SELENIUM_LOGIN_USER_ID'].present? && session[:user_id].blank?
|
||||||
|
session[:user_id] = ENV['FAKE_SELENIUM_LOGIN_USER_ID'].to_i
|
||||||
|
end
|
||||||
|
|
||||||
# logger.debug 'authentication_check'
|
# logger.debug 'authentication_check'
|
||||||
# logger.debug params.inspect
|
# logger.debug params.inspect
|
||||||
# logger.debug session.inspect
|
# logger.debug session.inspect
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue