Compare commits

...

36 Commits

Author SHA1 Message Date
Martin Edenhofer
809b8acada Updated to 2.8.1. 2019-02-14 07:51:12 +01:00
Thorsten Eckel
58f3f3fef8 Improved header handling. 2019-02-14 06:35:14 +01:00
André Bauer
b62a26c75a Follow up - bee2e2c956 Migrated from TravisCI to CircleCI - BUILD_SCRIPT path is wrong. 2019-02-13 10:54:25 +01:00
André Bauer
bee2e2c956 Migrated from TravisCI to CircleCI.
Signed-off-by: André Bauer <andre.bauer@kiwigrid.com>
2019-02-13 09:26:50 +01:00
Billy Zhou
48c6e6fe31 Fixed browser CI tests by downgrading selenium docker image to use older Firefox 64 2019-02-04 22:00:06 +08:00
André Bauer
a4fd51fdd4 cherry picked travis ci fixes from develop
Signed-off-by: André Bauer <monotek23@gmail.com>
2019-02-03 19:15:20 +01:00
André Bauer
0a33761db9 removed travis ci addon postgres 9.4 2019-02-03 19:13:39 +01:00
André Bauer
8b96ca3acf fixed travis mysql-server install
Signed-off-by: André Bauer <monotek23@gmail.com>
2019-02-01 20:55:53 +01:00
Billy Zhou
ad390e9f35 Merged PR #2440 - Request timeout while importing large attachments from Zendesk (thanks a lot to @anuzhin 😍). 2019-01-24 08:42:15 +01:00
Thorsten Eckel
844aa77a3a Refactoring: Make asset fetch more robust for records referring to deleted records. 2019-01-23 13:27:18 +01:00
Kevin Olbrich
81b38b603e Fix detection of local elasticsearch service
If elasticsearch is local and the URL was set via rake cli to "localhost" instead of "127.0.0.1", the service is not restarted.
This results in a broken state (search does not work, background tasks fail, etc.) if zammad is updated together with elasticsearch.
2019-01-23 10:04:38 +01:00
Thorsten Eckel
e2f1419f5e Refactoring: Use GitLab CI cache functionality to cache gem dependencies between pipelines/jobs/runners and replace obsolete zammad-application with zammad-ruby docker image. 2019-01-21 20:16:04 +01:00
Thorsten Eckel
acdd35af31 Refactoring: On premise CI infrastructure doesn't scale. 2019-01-20 20:19:19 +01:00
Martin Edenhofer
6bee27ddb8 Follow up for issue #2419 - Zammad Icon (Bird) on page top left disappears and does not come back - fixed test. 2019-01-08 23:16:09 +01:00
Martin Edenhofer
69afbcaf56 Fixed issue #2419 - Zammad Icon (Bird) on page top left disappears and does not come back. 2019-01-04 16:18:56 +01:00
Thorsten Eckel
6e29f40d98 Fixed failing CI: Date comparison checks for hour format 'HH' (as described in all comments) while App.PrettyDate.humanTime generates hour format 'H' for absolute type date strings. 2019-01-03 15:59:38 +01:00
Martin Edenhofer
1e5cc73625 Fixed issue #2420 - Unable to show ticket history. 2019-01-03 11:17:14 +01:00
Thorsten Eckel
fe7ba93908 Fixed bug: CI env rubocop fails because of missing dependencies. 2018-12-29 23:45:26 +01:00
Thorsten Eckel
246f644d0d Fixed issue #2418 - Missing table name quoting causes MySQL 8 incompatibility. 2018-12-29 23:09:16 +01:00
Thorsten Eckel
c39804acbd Fixed issue #2412 - i-doit version >= 1.12 integration requests fail with 'Can’t fetch objects from...' due to missing payload attribute 'id'. 2018-12-20 16:05:52 +01:00
Martin Edenhofer
629c561c28 Fixed issue #2405 - Unable to load Zammad in web browser, because of online notification of ticket which was already deleted in the meantime. 2018-12-18 13:41:38 +01:00
Billy Zhou
9bcd07c7db Fixed #2398 - Missing custom object in database causes error on export in time_accounting
(cherry picked from commit 3ca697dab7)
2018-12-17 04:25:11 +01:00
Thorsten Eckel
b36f6e2558 Applied required changes to run in GitLab docker CI env 🐳. 2018-12-15 08:54:41 +01:00
Thorsten Eckel
2e2b578ef7 Test stabilization: Creation of and communication to remote Selenium browser instance fails in load intense CI scenarios. 2018-12-15 08:52:18 +01:00
Thorsten Eckel
1736ce5b2a Refactoring: Improving logging of remote browser instance creation errors by logging earlier and adding exception to log. 2018-12-15 08:52:13 +01:00
Thorsten Eckel
41fa35c3ad Test stabilization: Timout for maintainance message to dissappear is too low and causes test to be flanky. 2018-12-15 08:52:07 +01:00
Thorsten Eckel
e3bc6b8845 Test stabilization: Reduced range of random number because to large numbers will cause the test to be flanky on CI env. 2018-12-15 08:51:59 +01:00
Thorsten Eckel
f25737a848 Test stabilization:
- Use standard way to navigate to manage view.
- Use standard way to wait for closing modal dialog.
- Selected user is not system state agnostic.
- Take screenshot if group selection fails.
2018-12-15 08:51:49 +01:00
Thorsten Eckel
b329dcfb4a Test stabilization: Non standard way to wait for modal close causes flanky test. 2018-12-15 08:51:26 +01:00
Martin Edenhofer
467ea4e417 Updated doc links for new structured admin documentation. 2018-12-14 10:49:59 +01:00
Martin Edenhofer
33c77b0986 Fixed issue #2397 - Unable to process emails without From (but with Sender) header. 2018-12-11 10:32:04 +01:00
Martin Edenhofer
ec9e35b5e2 Implemented issue #2395 - Search term is deleted after I opened a object/ticket. 2018-12-11 08:43:23 +01:00
Martin Edenhofer
cf663b9fdf Fixed/ improved test. 2018-12-10 16:11:23 +01:00
Martin Edenhofer
3f1b35c0eb Fixed issue #2394 - Jump to bottom after article has been created to show that the article has been saved. 2018-12-10 15:27:21 +01:00
Billy Zhou
272eecf94f Fix #2344 - Missing translation for quote header timestamp
(cherry picked from commit 6ff974e6ec)
2018-12-04 14:05:08 +01:00
Martin Edenhofer
1e5bc8fcbf Added CHANGES for 2.8. 2018-12-03 15:35:00 +01:00
70 changed files with 1115 additions and 751 deletions

67
.circleci/config.yml Normal file
View File

@@ -0,0 +1,67 @@
version: 2.1
jobs:
lint-scripts:
docker:
- image: koalaman/shellcheck-alpine
steps:
- checkout
- run:
name: lint-scripts
command: .circleci/lint-scripts.sh
install-mysql:
docker:
- image: circleci/ruby:2.4.4
- image: circleci/mysql:5.7-ram
command: --max_allowed_packet=64MB
environment:
MYSQL_DATABASE: zammad_test
MYSQL_USER: zammad_test
MYSQL_PASSWORD: zammad_test
environment:
RAILS_ENV: test
steps:
- checkout
- run:
name: install-mysql
command: .circleci/install.sh
install-postgresql:
docker:
- image: circleci/ruby:2.4.4
- image: circleci/postgres:11-ram
environment:
POSTGRES_DB: zammad_test
POSTGRES_USER: zammad_test
POSTGRES_PASSWORD: zammad_test
environment:
RAILS_ENV: test
steps:
- checkout
- run:
name: install-postgresql
command: .circleci/install.sh
docker-image-build:
machine: true
steps:
- checkout
- run:
name: docker-image-build
command: .circleci/docker-image-build.sh
workflows:
version: 2
install_unittest_dockerbuild:
jobs:
- lint-scripts
- install-mysql:
requires:
- lint-scripts
- install-postgresql:
requires:
- lint-scripts
- docker-image-build:
requires:
- install-mysql
- install-postgresql

39
.circleci/docker-image-build.sh Executable file
View File

@@ -0,0 +1,39 @@
#!/usr/bin/env bash
#
# build zammads docker & docker-compose images
set -o errexit
set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
GITHUB_REPO_USER="zammad"
ZAMMAD_VERSION="$(git describe --tags | sed -e 's/-[a-z0-9]\{8,\}.*//g')"
export ZAMMAD_VERSION
if [ "${CIRCLE_BRANCH}" == 'develop' ]; then
BUILD_SCRIPT="scripts/build_image.sh"
DOCKER_REPOSITORY="zammad"
export DOCKER_REPOSITORY
GITHUB_REPOSITORY="zammad-docker"
elif [ "${CIRCLE_BRANCH}" == 'stable' ]; then
BUILD_SCRIPT="hooks/build"
DOCKER_REPOSITORY="zammad-docker-compose"
export DOCKER_REPOSITORY
GITHUB_REPOSITORY="zammad-docker-compose"
else
echo "branch is ${CIRCLE_BRANCH}... no docker image build needed..."
exit 0
fi
# dockerhub auth
echo "${DOCKER_PASSWORD}" | docker login --username="${DOCKER_USERNAME}" --password-stdin
# clone docker repo
git clone https://github.com/"${GITHUB_REPO_USER}"/"${GITHUB_REPOSITORY}"
# enter dockerfile dir
cd "${REPO_ROOT}/${GITHUB_REPOSITORY}"
# build & push docker image
# shellcheck disable=SC1090
source "${REPO_ROOT}/${GITHUB_REPOSITORY}/${BUILD_SCRIPT}"

41
.circleci/install.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
#
# install & unit test zammad
#
set -o errexit
set -o pipefail
DB_CONFIG="test:\n adapter: postgresql\n database: zammad_test\n host: 127.0.0.1\n pool: 50\n timeout: 5000\n encoding: utf8\n username: zammad_test\n password: zammad_test"
# install build dependencies
sudo apt-get update
sudo apt-get install -y --no-install-recommends autoconf automake autotools-dev bison build-essential curl git-core libffi-dev libgdbm-dev libgmp-dev libmariadbclient-dev-compat libncurses5-dev libreadline-dev libsqlite3-dev libssl-dev libtool libxml2-dev libxslt1-dev libyaml-0-2 libyaml-dev patch pkg-config postfix sqlite3 zlib1g-dev
if [ "${CIRCLE_JOB}" == "install-mysql" ]; then
DB_ADAPTER="mysql2"
INSTALL_OPTION="postgres"
elif [ "${CIRCLE_JOB}" == "install-postgresql" ]; then
DB_ADAPTER="postgresql"
INSTALL_OPTION="mysql"
else
echo "nothing to do for circle ci job ${CIRCLE_JOB}..."
exit 0
fi
# create db config
echo -e "${DB_CONFIG}" | sed "s/adapter: postgresql/adapter: ${DB_ADAPTER}/g" > config/database.yml
# install zammad
bundle install --without "${INSTALL_OPTION}"
# unit tests
bundle exec rubocop
rake db:migrate
rake db:seed
bundle exec rspec -t ~type:system
bundle exec rake db:environment:set RAILS_ENV=test
rake db:reset
rake test:units
ruby -I test/ test/integration/object_manager_test.rb
ruby -I test/ test/integration/package_test.rb

18
.circleci/lint-scripts.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
#
# lint bash scripts
#
set -o errexit
CONFIG_DIR="./.circleci"
TMP_FILE="$(mktemp)"
find "${CONFIG_DIR}" -type f -name "*.sh" > "${TMP_FILE}"
while read -r FILE; do
echo lint "${FILE}"
shellcheck -x "${FILE}"
done < "${TMP_FILE}"
rm "${TMP_FILE}"

View File

@@ -1,6 +1,23 @@
variables:
Z_LOCALES: "en-us:de-de"
image: registry.znuny.com/docker/zammad-ruby:2.4.4
# Global variables added to the ENV of each job
variables:
# for faster translation loading
Z_LOCALES: "en-us:de-de"
# Browser tests and variables
RAILS_SERVE_STATIC_FILES: "true"
REMOTE_URL: "http://selenium:24444/wd/hub"
TZ: "Europe/London"
# docker elgalu/selenium variables for parallel browser instance creation
MAX_INSTANCES: "50"
MAX_SESSIONS: "50"
# docker-imap-devel variables required for browser tests
MAILNAME: "mail.test.dc.zammad.com"
MAILBOX_INIT: "zammad@mail.test.dc.zammad.com:zammad"
MAIL_ADDRESS: "zammad@mail.test.dc.zammad.com"
MAIL_PASS: "zammad"
# Artifacts are stored for failed jobs for 2 days
.artifacts_error_template: &artifacts_error
artifacts:
expire_in: 2 days
@@ -9,13 +26,43 @@ variables:
- tmp/screenshot*
- log/*.log
before_script:
- ruby -v
- which ruby
- env
- test -n "$RNAME" && script/build/test_db_config.sh
- test -n "$RNAME" && bundle install -j $(nproc)
# Workaround to enable usage of mixed SSH and Docker GitLab CI runners
.docker_env_template: &docker_env
tags:
- docker
# Workaround for blocked port 25 access on cloud provider infrastructure
.requires_mail_port_access_template: &requires_mail_port_access
tags:
- mail
.base_env_template: &base_env
<<: *docker_env
<<: *artifacts_error
# General required Docker services for different DB envs
.services_mysql_template: &services_mysql
services:
- name: registry.znuny.com/docker/zammad-mysql:latest
alias: mysql
.services_postgresql_template: &services_postgresql
services:
- name: registry.znuny.com/docker/zammad-postgresql:latest
alias: postgresql
# Cache gems in between jobs and pipelines
cache:
key: "ruby24"
paths:
- vendor/ruby
# Initialize application env
before_script:
- bundle install -j $(nproc) --path vendor
- bundle exec ruby script/build/database_config.rb
# Stages
stages:
- pre
- test
@@ -24,31 +71,33 @@ stages:
# pre stage
pre:rubocop:
# Workaround to enable usage of mixed SSH and Docker GitLab CI runners
.pre_stage_template: &pre_stage
<<: *docker_env
stage: pre
tags:
- core
before_script:
- '' # disable before_script for pre "non-application" env
pre:rubocop:
<<: *pre_stage
script:
- bundle install -j $(nproc) --path vendor
- bundle exec rubocop
pre:coffeelint:
stage: pre
tags:
- core
<<: *pre_stage
script:
- coffeelint app/
pre:bundle-audit:
stage: pre
tags:
- core
<<: *pre_stage
script:
- gem install bundler-audit
- bundle-audit update
- bundle-audit
pre:github:
stage: pre
<<: *pre_stage
tags:
- deploy
script:
@@ -59,253 +108,189 @@ pre:github:
## RSpec
.script_rspec_template: &script_rspec_definition
<<: *artifacts_error
<<: *base_env
variables:
RAILS_ENV: "test"
script:
- rake zammad:db:init
- bundle exec rake zammad:db:init
- bundle exec rspec
- rake db:drop
test:rspec:mysql:
stage: test
tags:
- core
- mysql
<<: *services_mysql
<<: *script_rspec_definition
test:rspec:postgresql:
stage: test
tags:
- core
- postgresql
<<: *services_postgresql
<<: *script_rspec_definition
## Unit and Controller tests
.script_unit_template: &script_unit_definition
<<: *artifacts_error
<<: *base_env
variables:
RAILS_ENV: "test"
script:
- rake zammad:db:init
- rake test:units
- ruby -I test/ test/integration/object_manager_test.rb
- ruby -I test/ test/integration/object_manager_attributes_controller_test.rb
- ruby -I test/ test/integration/package_test.rb
- ruby -I test/ test/integration/monitoring_controller_test.rb
- rake db:drop
- bundle exec rake zammad:db:init
- bundle exec rake test:units
- bundle exec ruby -I test/ test/integration/object_manager_test.rb
- bundle exec ruby -I test/ test/integration/object_manager_attributes_controller_test.rb
- bundle exec ruby -I test/ test/integration/package_test.rb
- bundle exec ruby -I test/ test/integration/monitoring_controller_test.rb
test:unit:mysql:
stage: test
tags:
- core
- mysql
<<: *services_mysql
<<: *script_unit_definition
test:unit:postgresql:
stage: test
tags:
- core
- postgresql
<<: *services_postgresql
<<: *script_unit_definition
## Integration tests
test:integration:email_helper_deliver:
<<: *artifacts_error
.test_integration_template: &test_integration_definition
<<: *base_env
<<: *services_postgresql
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
test:integration:email_helper_deliver:
<<: *test_integration_definition
<<: *requires_mail_port_access
script:
- rake zammad:db:unseeded
- ruby -I test/ test/integration/email_helper_test.rb
- ruby -I test/ test/integration/email_deliver_test.rb
- ruby -I test/ test/integration/email_keep_on_server_test.rb
- rake db:drop
- bundle exec rake zammad:db:unseeded
- bundle exec ruby -I test/ test/integration/email_helper_test.rb
- bundle exec ruby -I test/ test/integration/email_deliver_test.rb
- bundle exec ruby -I test/ test/integration/email_keep_on_server_test.rb
test:integration:facebook:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core-facebook
<<: *test_integration_definition
script:
- rake zammad:db:init
- ruby -I test/ test/integration/facebook_test.rb
- rake db:drop
- bundle exec rake zammad:db:init
- bundle exec ruby -I test/ test/integration/facebook_test.rb
allow_failure: true
test:integration:geo:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
<<: *test_integration_definition
script:
- rake zammad:db:unseeded
- ruby -I test/ test/integration/geo_calendar_test.rb
- ruby -I test/ test/integration/geo_location_test.rb
- ruby -I test/ test/integration/geo_ip_test.rb
- rake db:drop
- bundle exec rake zammad:db:unseeded
- bundle exec ruby -I test/ test/integration/geo_calendar_test.rb
- bundle exec ruby -I test/ test/integration/geo_location_test.rb
- bundle exec ruby -I test/ test/integration/geo_ip_test.rb
test:integration:user_agent:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
<<: *test_integration_definition
script:
- rake zammad:db:unseeded
- ruby -I test/ test/integration/user_agent_test.rb
- bundle exec rake zammad:db:unseeded
- bundle exec ruby -I test/ test/integration/user_agent_test.rb
- export ZAMMAD_PROXY_TEST=true
- ruby -I test/ test/integration/user_agent_test.rb
- rake db:drop
- bundle exec ruby -I test/ test/integration/user_agent_test.rb
allow_failure: true
test:integration:user_device:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
<<: *test_integration_definition
script:
- rake zammad:db:unseeded
- ruby -I test/ test/integration/user_device_controller_test.rb
- rake db:drop
- bundle exec rake zammad:db:unseeded
- bundle exec ruby -I test/ test/integration/user_device_controller_test.rb
test:integration:slack:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
<<: *test_integration_definition
script:
- rake zammad:db:unseeded
- bundle exec rake zammad:db:unseeded
- echo "gem 'slack-api'" >> Gemfile.local
- bundle install -j $(nproc)
- ruby -I test test/integration/slack_test.rb
- rake db:drop
- bundle exec ruby -I test test/integration/slack_test.rb
test:integration:clearbit:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
<<: *test_integration_definition
script:
- rake zammad:db:unseeded
- ruby -I test test/integration/clearbit_test.rb
- rake db:drop
- bundle exec rake zammad:db:unseeded
- bundle exec ruby -I test test/integration/clearbit_test.rb
allow_failure: true
test:integration:telegram:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
<<: *test_integration_definition
script:
- rake zammad:db:init
- ruby -I test test/integration/telegram_controller_test.rb
- rake db:drop
- bundle exec rake zammad:db:init
- bundle exec ruby -I test test/integration/telegram_controller_test.rb
test:integration:twilio:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
<<: *test_integration_definition
script:
- rake zammad:db:init
- ruby -I test test/integration/twilio_sms_controller_test.rb
- rake db:drop
- bundle exec rake zammad:db:init
- bundle exec ruby -I test test/integration/twilio_sms_controller_test.rb
test:integration:idoit:
<<: *artifacts_error
stage: test
variables:
RAILS_ENV: "test"
tags:
- core
<<: *test_integration_definition
script:
- rake zammad:db:init
- ruby -I test test/integration/idoit_controller_test.rb
- rake db:drop
- bundle exec rake zammad:db:init
- bundle exec ruby -I test test/integration/idoit_controller_test.rb
### Elasticsearch
.script_elasticsearch_template: &script_elasticsearch_definition
<<: *artifacts_error
<<: *base_env
stage: test
variables:
RAILS_ENV: "test"
ES_INDEX_RAND: "true"
ES_URL: "http://localhost:9200"
ES_URL: "http://elasticsearch:9200"
script:
- rake zammad:db:unseeded
- ruby -I test/ test/integration/elasticsearch_active_test.rb
- ruby -I test/ test/integration/elasticsearch_test.rb
- ruby -I test/ test/integration/report_test.rb
- bundle exec rake zammad:db:unseeded
- bundle exec ruby -I test/ test/integration/elasticsearch_active_test.rb
- bundle exec ruby -I test/ test/integration/elasticsearch_test.rb
- bundle exec ruby -I test/ test/integration/report_test.rb
- bundle exec rspec --tag searchindex
- rake db:drop
test:integration:es_mysql:
tags:
- core
- mysql
<<: *script_elasticsearch_definition
services:
- name: registry.znuny.com/docker/zammad-mysql:latest
alias: mysql
- name: registry.znuny.com/docker/zammad-elasticsearch:latest
alias: elasticsearch
test:integration:es_postgresql:
tags:
- core
- postgresql
<<: *script_elasticsearch_definition
services:
- name: registry.znuny.com/docker/zammad-postgresql:latest
alias: postgresql
- name: registry.znuny.com/docker/zammad-elasticsearch:latest
alias: elasticsearch
### Zendesk
.script_integration_zendesk_template: &script_integration_zendesk_definition
<<: *artifacts_error
<<: *base_env
<<: *services_postgresql
stage: test
variables:
RAILS_ENV: "test"
script:
- rake zammad:db:unseeded
- ruby -I test/ test/integration/zendesk_import_test.rb
- rake db:drop
- bundle exec rake zammad:db:unseeded
- bundle exec ruby -I test/ test/integration/zendesk_import_test.rb
allow_failure: true
test:integration:zendesk_mysql:
tags:
- core
- mysql
<<: *services_mysql
<<: *script_integration_zendesk_definition
test:integration:zendesk_postgresql:
tags:
- core
- postgresql
<<: *services_postgresql
<<: *script_integration_zendesk_definition
### OTRS
.script_integration_otrs_template: &script_integration_otrs_definition
<<: *artifacts_error
<<: *base_env
stage: test
script:
- rake zammad:db:unseeded
- ruby -I test/ test/integration/otrs_import_test.rb
- rake db:drop
- bundle exec rake zammad:db:unseeded
- bundle exec ruby -I test/ test/integration/otrs_import_test.rb
.variables_integration_otrs_6_template: &variables_integration_otrs_6_definition
variables:
@@ -313,54 +298,45 @@ test:integration:zendesk_postgresql:
IMPORT_OTRS_ENDPOINT: "https://vz1185.test.znuny.com/otrs/public.pl?Action=ZammadMigrator"
test:integration:otrs_6_mysql:
tags:
- core
- mysql
<<: *services_mysql
<<: *script_integration_otrs_definition
<<: *variables_integration_otrs_6_definition
test:integration:otrs_6_postgresql:
tags:
- core
- postgresql
<<: *services_postgresql
<<: *script_integration_otrs_definition
<<: *variables_integration_otrs_6_definition
test:integration:otrs_5:
tags:
- core
<<: *services_postgresql
variables:
RAILS_ENV: "test"
IMPORT_OTRS_ENDPOINT: "http://vz1109.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
<<: *script_integration_otrs_definition
test:integration:otrs_4:
tags:
- core
<<: *services_postgresql
variables:
RAILS_ENV: "test"
IMPORT_OTRS_ENDPOINT: "http://vz383.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
<<: *script_integration_otrs_definition
test:integration:otrs_33:
tags:
- core
<<: *services_postgresql
variables:
RAILS_ENV: "test"
IMPORT_OTRS_ENDPOINT: "http://vz305.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
<<: *script_integration_otrs_definition
test:integration:otrs_32:
tags:
- core
<<: *services_postgresql
variables:
RAILS_ENV: "test"
IMPORT_OTRS_ENDPOINT: "http://vz382.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
<<: *script_integration_otrs_definition
test:integration:otrs_31:
tags:
- core
<<: *services_postgresql
variables:
RAILS_ENV: "test"
IMPORT_OTRS_ENDPOINT: "http://vz381.demo.znuny.com/otrs/public.pl?Action=ZammadMigrator"
@@ -371,15 +347,14 @@ test:integration:otrs_31:
## preparation (asset precompile)
browser:build:
<<: *base_env
<<: *services_postgresql
stage: test
tags:
- core
variables:
RAILS_ENV: "production"
script:
- rake zammad:db:unseeded
- rake assets:precompile
- rake db:drop
- bundle exec rake zammad:db:unseeded
- bundle exec rake assets:precompile
artifacts:
expire_in: 1 week
paths:
@@ -390,13 +365,11 @@ browser:build:
## Browser core tests
.variables_browser_template: &variables_browser_definition
RAILS_SERVE_STATIC_FILES: "true"
RAILS_ENV: "production"
BROWSER_URL: "http://$IP:$BROWSER_PORT"
APP_RESTART_CMD: "rake zammad:ci:app:restart"
APP_RESTART_CMD: "bundle exec rake zammad:ci:app:restart"
.test_browser_core_template: &test_browser_core_definition
<<: *artifacts_error
<<: *base_env
stage: browser-core
dependencies:
- browser:build
@@ -405,47 +378,42 @@ browser:build:
test:browser:integration:api_client_ruby:
<<: *test_browser_core_definition
<<: *services_postgresql
variables:
<<: *variables_browser_definition
TEST_URL: "http://$IP:$BROWSER_PORT/"
tags:
- browser
script:
- RAILS_ENV=test rake db:create
- RAILS_ENV=test bundle exec rake db:create
- cp contrib/auto_wizard_test.json auto_wizard.json
- rake zammad:ci:test:start
- git clone git@github.com:zammad/zammad-api-client-ruby.git || rake zammad:ci:test:fail
- bundle exec rake zammad:ci:test:start
- git clone https://github.com/zammad/zammad-api-client-ruby.git
- cd zammad-api-client-ruby
- bundle install -j $(nproc)
- bundle exec rspec || (cd .. && rake zammad:ci:test:fail)
- cd .. && rake zammad:ci:test:stop
- bundle exec rspec
test:browser:integration:api_client_php:
<<: *test_browser_core_definition
<<: *services_postgresql
variables:
<<: *variables_browser_definition
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL: "http://$IP:$BROWSER_PORT"
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL: "http://localhost:3000"
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME: "master@example.com"
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD: "test"
tags:
- browser
script:
- RAILS_ENV=test rake db:create
- rake zammad:ci:test:start zammad:setup:auto_wizard
- git clone git@github.com:zammad/zammad-api-client-php || rake zammad:ci:test:fail
- RAILS_ENV=test bundle exec rake db:create
- bundle exec rake zammad:ci:test:start zammad:setup:auto_wizard
- git clone https://github.com/zammad/zammad-api-client-php.git
- php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
- php composer-setup.php --install-dir=/usr/local/bin
- ln -s /usr/local/bin/composer.phar /usr/local/bin/composer
- cd zammad-api-client-php
- composer install
- vendor/bin/phpunit || (cd .. && rake zammad:ci:test:fail)
- cd .. && rake zammad:ci:test:stop
- vendor/bin/phpunit
### Browser test slices
#### Templates
.script_browser_slice_template: &script_browser_slice_definition
stage: browser-core
dependencies:
- browser:build
script:
# temporary workaround to check Yahoo! mailbox only in test:browser:core:ff_3_* tests
- if [[ $CI_JOB_NAME != test:browser:core:ff_3_* ]]; then unset MAILBOX_AUTO1 ; fi
@@ -454,24 +422,35 @@ test:browser:integration:api_client_php:
- if [[ $CI_JOB_NAME != test:browser:core:ff_3_* ]]; then unset MAILBOX_MANUAL2 ; fi
- env
- script/build/test_slice_tests.sh $TEST_SLICE
- RAILS_ENV=test rake db:create
- rake zammad:ci:test:start[with_elasticsearch]
- rake test:browser || rake zammad:ci:test:fail
- rake zammad:ci:test:stop
- RAILS_ENV=test bundle exec rake db:create
- bundle exec rake zammad:ci:test:start[with_elasticsearch]
- bundle exec rake test:browser
.test_browser_core_postgresql_template: &test_browser_core_postgresql_definition
<<: *test_browser_core_definition
tags:
- browser
- postgresql
<<: *script_browser_slice_definition
services:
- name: registry.znuny.com/docker/zammad-postgresql:latest
alias: postgresql
- name: registry.znuny.com/docker/zammad-elasticsearch:latest
alias: elasticsearch
- name: docker.io/elgalu/selenium:3.141.59-p8
alias: selenium
- name: registry.znuny.com/docker/docker-imap-devel:latest
alias: mail
.test_browser_core_mysql_template: &test_browser_core_mysql_definition
<<: *test_browser_core_definition
tags:
- browser
- mysql
<<: *script_browser_slice_definition
services:
- name: registry.znuny.com/docker/zammad-mysql:latest
alias: mysql
- name: registry.znuny.com/docker/zammad-elasticsearch:latest
alias: elasticsearch
- name: docker.io/elgalu/selenium:3.141.59-p8
alias: selenium
- name: registry.znuny.com/docker/docker-imap-devel:latest
alias: mail
#### Firefox
@@ -491,6 +470,7 @@ test:browser:core:ff_2_mysql:
test:browser:core:ff_3_mysql:
<<: *test_browser_core_mysql_definition
<<: *requires_mail_port_access
variables:
<<: *variables_browser_definition
BROWSER: "firefox"
@@ -533,6 +513,7 @@ test:browser:core:ff_2_postgresql:
test:browser:core:ff_3_postgresql:
<<: *test_browser_core_postgresql_definition
<<: *requires_mail_port_access
variables:
<<: *variables_browser_definition
BROWSER: "firefox"
@@ -647,28 +628,34 @@ test:browser:core:chrome_6_postgresql:
### Auto wizard
.auto_wizard_services_template: &auto_wizard_services
services:
- name: registry.znuny.com/docker/zammad-postgresql:latest
alias: postgresql
- name: docker.io/elgalu/selenium:3.141.59-p8
alias: selenium
.test_browser_integration_template: &test_browser_integration_definition
<<: *artifacts_error
<<: *base_env
<<: *auto_wizard_services
stage: browser-integration
dependencies:
- browser:build
.script_integration_auto_wizard_template: &script_integration_auto_wizard_definition
script:
- RAILS_ENV=test rake db:create
- RAILS_ENV=test bundle exec rake db:create
- cp $AUTO_WIZARD_FILE auto_wizard.json
- rake zammad:ci:test:start[with_elasticsearch]
- ruby -I test/ $TEST_FILE || rake zammad:ci:test:fail
- rake zammad:ci:test:stop
- bundle exec rake zammad:ci:test:start
- bundle exec ruby -I test/ $TEST_FILE
.browser_core_auto_wizard_template: &browser_core_auto_wizard_definition
<<: *test_browser_core_definition
<<: *auto_wizard_services
<<: *script_integration_auto_wizard_definition
test:browser:autowizard_chrome:
<<: *browser_core_auto_wizard_definition
tags:
- browser
variables:
<<: *variables_browser_definition
BROWSER: "chrome"
@@ -677,8 +664,6 @@ test:browser:autowizard_chrome:
test:browser:autowizard_ff:
<<: *browser_core_auto_wizard_definition
tags:
- browser
variables:
<<: *variables_browser_definition
BROWSER: "firefox"
@@ -693,9 +678,6 @@ test:browser:autowizard_ff:
test:browser:integration:twitter_chrome:
<<: *browser_integration_auto_wizard_definition
tags:
- browser
- twitter
variables:
<<: *variables_browser_definition
BROWSER: "chrome"
@@ -704,9 +686,6 @@ test:browser:integration:twitter_chrome:
test:browser:integration:twitter_ff:
<<: *browser_integration_auto_wizard_definition
tags:
- browser
- twitter
variables:
<<: *variables_browser_definition
BROWSER: "firefox"
@@ -715,9 +694,6 @@ test:browser:integration:twitter_ff:
test:browser:integration:facebook_chrome:
<<: *browser_integration_auto_wizard_definition
tags:
- browser
- facebook
variables:
<<: *variables_browser_definition
BROWSER: "chrome"
@@ -726,9 +702,6 @@ test:browser:integration:facebook_chrome:
test:browser:integration:facebook_ff:
<<: *browser_integration_auto_wizard_definition
tags:
- browser
- facebook
variables:
<<: *variables_browser_definition
BROWSER: "firefox"
@@ -737,8 +710,6 @@ test:browser:integration:facebook_ff:
test:browser:integration:idoit_chrome:
<<: *browser_integration_auto_wizard_definition
tags:
- browser
variables:
<<: *variables_browser_definition
BROWSER: "chrome"
@@ -749,17 +720,15 @@ test:browser:integration:idoit_chrome:
.variables_browser_import_template: &variables_browser_import_definition
BROWSER: "chrome"
BROWSER_URL: "http://$IP:$BROWSER_PORT"
RAILS_SERVE_STATIC_FILES: "true"
RAILS_ENV: "production"
.browser_integration_import_template: &browser_integration_import_definition
<<: *test_browser_integration_definition
tags:
- browser
script:
- RAILS_ENV=test rake db:create
- rake zammad:ci:test:start
- ruby -I test/ $TEST_FILE
- rake zammad:ci:test:stop
- RAILS_ENV=test bundle exec rake db:create
- bundle exec rake zammad:ci:test:start
- bundle exec ruby -I test/ $TEST_FILE
test:browser:integration:otrs_chrome:
<<: *browser_integration_import_definition

View File

@@ -10,6 +10,7 @@ AllCops:
- 'bin/rake'
- 'bin/spring'
- 'db/schema.rb'
- 'vendor/**/*'
Rails:
Enabled: true

View File

@@ -1,74 +0,0 @@
dist: trusty
sudo: false
notifications:
email:
- me+tv@zammad.com
env:
- DB=mysql
- DB=postgresql
addons:
postgresql: "9.4"
apt:
packages:
- autoconf
- automake
- autotools-dev
- bison
- build-essential
- curl
- git-core
- libffi-dev
- libgdbm-dev
- libgmp-dev
- libmysqlclient-dev
- libncurses5-dev
- libreadline6-dev
- libsqlite3-dev
- libssl-dev
- libtool
- libxml2-dev
- libxslt1-dev
- libyaml-0-2
- libyaml-dev
- mysql-client-5.6
- mysql-client-core-5.6
- mysql-server-5.6
- patch
- pkg-config
- postfix
- sqlite3
- zlib1g-dev
services:
- postgresql
- mysql
language: ruby
rvm:
- 2.4.4
before_install:
- gem update --system # Due to: https://github.com/travis-ci/travis-ci/issues/8978
- git fetch --unshallow
- if [ "${DB}" = "mysql" ]; then mysql -u root -e "CREATE USER 'some_user'@'localhost' IDENTIFIED BY 'some_pass';"; fi
- if [ "${DB}" = "mysql" ]; then mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'some_user'@'localhost';"; fi
- if [ "${DB}" = "mysql" ]; then cp contrib/travis-ci.org/database.yml.test-mysql config/database.yml; fi
- if [ "${DB}" = "postgresql" ]; then psql -c 'create database zammad_test;' -U postgres; fi
- if [ "${DB}" = "postgresql" ]; then cp contrib/travis-ci.org/database.yml.test-postgresql config/database.yml; fi
- export RAILS_ENV=test
- export ZAMMAD_VERSION=$(git describe --tags | sed -e 's/-[a-z0-9]\{8,\}.*//g')
install:
- if [ "${DB}" = "mysql" ]; then bundle install --without postgres; fi
- if [ "${DB}" = "postgresql" ]; then bundle install; fi
script:
- bundle exec rubocop
- rake db:create
- rake db:migrate
- rake db:seed
- bundle exec rspec
- bundle exec rake db:environment:set RAILS_ENV=test
- rake db:reset
- rake test:units
- ruby -I test/ test/integration/object_manager_test.rb
- ruby -I test/ test/integration/package_test.rb
after_success:
- if [ "${DB}" = "mysql" ]; then contrib/travis-ci.org/trigger_docker_image_build.sh; fi
- if [ "${DB}" = "mysql" ]; then contrib/travis-ci.org/trigger_docker_image_compose_build.sh; fi
- if [ "${DB}" = "mysql" ]; then contrib/travis-ci.org/trigger_docker_image_univention_build.sh; fi

View File

@@ -1,15 +1,51 @@
# Change Log
## [2.8.0](https://github.com/zammad/zammad/tree/2.8.0) (2018-xx-xx)
[Full Changelog](https://github.com/zammad/zammad/compare/2.7.0...2.8.0)
## [2.8.1](https://github.com/zammad/zammad/tree/2.8.1) (2019-02-14)
[Full Changelog](https://github.com/zammad/zammad/compare/2.8.0...2.8.1)
**Implemented enhancements:**
**Fixed bugs:**
- Increased timeout for big attachments [2440](https://github.com/zammad/zammad/pull/2440)
- Zammad Icon (Bird) on page top left disappears and does not come back [2419](https://github.com/zammad/zammad/issues/2419) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[bug](https://github.com/zammad/zammad/labels/bug)] [[regression](https://github.com/zammad/zammad/labels/regression)]
- Unable to show ticket history [2420](https://github.com/zammad/zammad/issues/2420) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Missing table name quoting causes MySQL 8 incompatibility [2418](https://github.com/zammad/zammad/issues/2418) [[bug](https://github.com/zammad/zammad/labels/bug)]
- i-doit version >= 1.12 integration requests fail with 'Can’t fetch objects from...' [2412](https://github.com/zammad/zammad/issues/2412) [[bug](https://github.com/zammad/zammad/labels/bug)] [[integration](https://github.com/zammad/zammad/labels/integration)]
- Unable to load Zammad in web browser, because of online notification of ticket which was already deleted in the meantime [2405](https://github.com/zammad/zammad/issues/2405) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Missing custom object in database causes error on export in time_accounting [2398](https://github.com/zammad/zammad/issues/2398) [[bug](https://github.com/zammad/zammad/labels/bug)] [[time accounting](https://github.com/zammad/zammad/labels/time accounting)]
- Unable to process emails without From (but with Sender) header. [2397](https://github.com/zammad/zammad/issues/2397) [[bug](https://github.com/zammad/zammad/labels/bug)] [[mail processing](https://github.com/zammad/zammad/labels/mail processing)]
- Search term is deleted after I opened a object/ticket [2395](https://github.com/zammad/zammad/issues/2395) [[regression](https://github.com/zammad/zammad/labels/regression)]
- Jump to bottom after article has been created to show that the article has been saved. [2394](https://github.com/zammad/zammad/issues/2394)
- Missing translation for Full-Quote-Text "on xy wrote" [2344](https://github.com/zammad/zammad/issues/2344) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[translation](https://github.com/zammad/zammad/labels/translation)]
## [2.8.0](https://github.com/zammad/zammad/tree/2.8.0) (2018-12-03)
[Full Changelog](https://github.com/zammad/zammad/compare/2.7.0...2.8.0)
**Implemented enhancements:**
- Twitter Account Activity API support [2023](https://github.com/zammad/zammad/issues/2023) [[channel](https://github.com/zammad/zammad/labels/channel)] [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- On large database indexes at missing [2368](https://github.com/zammad/zammad/issues/2368) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Elasticsearch payload is going to big if a ticket has more then 9000 articles [2345](https://github.com/zammad/zammad/issues/2345) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Logs flooded with article autosave data [2363](https://github.com/zammad/zammad/issues/2363) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Increased popup width for text modules or placeholder list [2335](https://github.com/zammad/zammad/pull/2335) [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[frontend](https://github.com/zammad/zammad/labels/frontend)]
**Fixed bugs:**
- Unable to process html email with 2k links in an acceptable time [2374](https://github.com/zammad/zammad/issues/2374) [[bug](https://github.com/zammad/zammad/labels/bug)] [[needs verification](https://github.com/zammad/zammad/labels/needs verification)]
- Column width is resized to a non readable minimum [2031](https://github.com/zammad/zammad/issues/2031) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Zendesk-Import fails for User & Organizations if Plan is below "Team" [2262](https://github.com/zammad/zammad/issues/2262) [[bug](https://github.com/zammad/zammad/labels/bug)] [[import](https://github.com/zammad/zammad/labels/import)]
- Update italian translations for chat messages [2359](https://github.com/zammad/zammad/pull/2359)
- Chat does not work due to errors in WebSocket (Database connection) [2353](https://github.com/zammad/zammad/issues/2353) [[bug](https://github.com/zammad/zammad/labels/bug)]
- selecting the overviews on the I Pad does not work [2330](https://github.com/zammad/zammad/issues/2330) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Update de.html.erb [2348](https://github.com/zammad/zammad/pull/2348)
- Lost DB connection causes jobs to not be processed anymore [2343](https://github.com/zammad/zammad/issues/2343) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Role-Filter shows inactive Roles [2332](https://github.com/zammad/zammad/issues/2332) [[admin area](https://github.com/zammad/zammad/labels/admin area)] [[bug](https://github.com/zammad/zammad/labels/bug)]
- IMAP fetch stops because of request timeout [2321](https://github.com/zammad/zammad/issues/2321) [[bug](https://github.com/zammad/zammad/labels/bug)] [[channel](https://github.com/zammad/zammad/labels/channel)]
- Object country already exists [2333](https://github.com/zammad/zammad/issues/2333) [[bug](https://github.com/zammad/zammad/labels/bug)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
- Upgrade to Zammad 2.7 was not possible (migration 20180220171219 CheckForObjectAttributes failed) [2318](https://github.com/zammad/zammad/issues/2318) [[bug](https://github.com/zammad/zammad/labels/bug)] [[migration](https://github.com/zammad/zammad/labels/migration)]
- Unable to import (update) users with email addresses als lookup index written in upper letters [2329](https://github.com/zammad/zammad/issues/2329) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Websocket messages are not working correctly via Ajax long polling (e. g. multiple browser tabs can get opened with one session). [2327](https://github.com/zammad/zammad/issues/2327) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Improved error handling of invalid tag for text modules and add current time format (to not use 2018-10-31T08:02:21.917Z format) [2324](https://github.com/zammad/zammad/issues/2324) [[bug](https://github.com/zammad/zammad/labels/bug)]
- incorrect notification when closing a tab after setting up an object [2042](https://github.com/zammad/zammad/issues/2042) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend](https://github.com/zammad/zammad/labels/frontend)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)]
- Ticket list in overview of customer and organisation random order [2296](https://github.com/zammad/zammad/issues/2296) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend](https://github.com/zammad/zammad/labels/frontend)]
- ObjectManager Attribute Select used in trigger will show internal ID instead of value [1568](https://github.com/zammad/zammad/issues/1568) [[bug](https://github.com/zammad/zammad/labels/bug)] [[object manager attribute](https://github.com/zammad/zammad/labels/object manager attribute)] [[trigger](https://github.com/zammad/zammad/labels/trigger)]
- Can't forward E-Mail in special cases within Firefox [2305](https://github.com/zammad/zammad/issues/2305) [[bug](https://github.com/zammad/zammad/labels/bug)] [[frontend](https://github.com/zammad/zammad/labels/frontend)]

View File

@@ -2,8 +2,8 @@
Zammad is a web based open source helpdesk/customer support system with many
features to manage customer communication via several channels like telephone,
facebook, twitter, chat and e-mails. It is distributed under the GNU AFFERO
General Public License (AGPL).
facebook, twitter, chat and e-mails. It is distributed under version 3 of the
GNU AFFERO General Public License (GNU AGPLv3).
Do you receive many e-mails and want to answer them with a team of agents?
@@ -11,7 +11,7 @@ You're going to love Zammad!
## Status
- Build: [![Build Status](https://travis-ci.org/zammad/zammad.svg?branch=develop)](https://travis-ci.org/zammad/zammad)
- Build: [![Build Status](https://circleci.com/gh/zammad/zammad.svg?style=svg)](https://circleci.com/gh/zammad/zammad)
- Code: [![Code Climate](https://codeclimate.com/github/zammad/zammad/badges/gpa.svg)](https://codeclimate.com/github/zammad/zammad) [![Coverage Status](https://coveralls.io/repos/github/zammad/zammad/badge.svg)](https://coveralls.io/github/zammad/zammad)
- Docs: [![Documentation Status](https://readthedocs.org/projects/zammad/badge/)](https://docs.zammad.org)
- Issues: [![Percentage of issues still open](http://isitmaintained.com/badge/open/zammad/zammad.svg)](https://github.com/zammad/zammad/issues "Percentage of issues still open") [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/zammad/zammad.svg)](https://github.com/zammad/zammad/issues?q=is%3Aissue+is%3Aclosed "Average time to resolve an issue")
@@ -44,4 +44,3 @@ https://zammad.org/participate
Thanks! ❤️ ❤️ ❤️
Your Zammad Team

View File

@@ -23,17 +23,26 @@ class App.TicketMerge extends App.ControllerModal
@render()
)
onShown: (e) =>
super
later = =>
if @tableCustomerTickets
@tableCustomerTickets.show()
if @tableRecentViewedTickets
@tableRecentViewedTickets.show()
@delay(later, 300)
content: =>
content = $( App.view('agent_ticket_merge')() )
new App.TicketList(
@tableCustomerTickets = new App.TicketList(
tableId: 'ticket-merge-customer-tickets'
el: content.find('#ticket-merge-customer-tickets')
ticket_ids: @ticket_ids_by_customer
radio: true
)
new App.TicketList(
@tableRecentViewedTickets = new App.TicketList(
tableId: 'ticket-merge-recent-tickets'
el: content.find('#ticket-merge-recent-tickets')
ticket_ids: @ticket_ids_recent_viewed

View File

@@ -18,7 +18,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
'focus #global-search': 'searchFocus'
'blur #global-search': 'searchBlur'
'keyup #global-search': 'listNavigate'
'click .js-global-search-result': 'emptyAndClose'
'click .js-global-search-result': 'emptyAndCloseDelayed'
'click .js-details-link': 'openExtendedSearch'
'change .js-menu .js-switch input': 'switch'
@@ -162,7 +162,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
renderResult: (result = [], noChange) =>
if noChange
return
@removePopovers()
# remove result if not result exists
@@ -202,18 +202,18 @@ class App.Navigation extends App.ControllerWidgetPermanent
$('#app').append @notificationWidget.el
searchFocus: (e) =>
@query = '' # reset query cache
@clearDelay('emptyAndCloseDelayed')
@throttledSearch()
App.PopoverProvidable.anyPopoversDestroy()
@searchContainer.addClass('focused')
@selectAll(e)
searchBlur: (e) =>
# delay to be able to click x
update = =>
query = @searchInput.val().trim()
if !query
@emptyAndClose()
return
# delay to be able to "click/execute" x if query is ''
update = =>
if @searchInput.val().trim() is ''
@emptyAndClose()
@delay(update, 100, 'removeFocused')
listNavigate: (e) =>
@@ -231,9 +231,9 @@ class App.Navigation extends App.ControllerWidgetPermanent
href = @$('.global-search-result .nav-tab.is-hover').attr('href')
if href
@navigate(href)
@emptyAndCloseDelayed()
else
@openExtendedSearch()
@emptyAndClose()
return
# on other keys, show result
@@ -277,16 +277,21 @@ class App.Navigation extends App.ControllerWidgetPermanent
@scrollToIfNeeded(prev, false)
emptyAndClose: =>
@andClose()
@andEmpty()
emptyAndCloseDelayed: =>
@andClose()
delay = =>
@andEmpty()
@delay(delay, 60000, 'emptyAndCloseDelayed')
andEmpty: =>
@query = ''
@searchInput.val('')
@searchContainer.removeClass('focused filled open no-match loading')
@globalSearch.close()
@delayedRemoveAnyPopover()
andClose: =>
@query = ''
@searchInput.blur()
@searchContainer.removeClass('open no-match loading')
@searchContainer.removeClass('focused filled open no-match loading')
@globalSearch.close()
@delayedRemoveAnyPopover()
@@ -301,9 +306,10 @@ class App.Navigation extends App.ControllerWidgetPermanent
@query = query
if @query == ''
@searchContainer.removeClass('open')
@searchContainer.removeClass('open loading')
return
@searchContainer.addClass('open')
@globalSearch.search(query: @query)
filterNavbar: (values, user, parent = null) ->
@@ -460,7 +466,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
if e
e.preventDefault()
query = @searchInput.val()
@searchInput.val('').blur()
@emptyAndClose()
if query
@navigate("#search/#{encodeURIComponent(query)}")
return

View File

@@ -239,6 +239,17 @@ class App.TicketZoom extends App.Controller
return if !@attributeBar
@attributeBar.start()
# scroll to article if given
scrollToPosition: (position, delay, article_id) =>
scrollToDelay = =>
if position is 'article'
@scrollToArticle(article_id)
@positionPageHeaderUpdate()
return
@scrollToBottom()
@positionPageHeaderUpdate()
@delay(scrollToDelay, delay, 'scrollToPosition')
pagePosition: (params = {}) =>
# remember for later
@@ -251,34 +262,23 @@ class App.TicketZoom extends App.Controller
article_id = @pagePositionData
@pagePositionData = undefined
# scroll to article if given
scrollToPosition = (position, delay) =>
scrollToDelay = =>
if position is 'article'
@scrollToArticle(article_id)
@positionPageHeaderUpdate()
return
@scrollToBottom()
@positionPageHeaderUpdate()
@delay(scrollToDelay, delay, 'scrollToPosition')
# trigger shown to article
if !@shown
@shown = true
App.Event.trigger('ui::ticket::shown', { ticket_id: @ticket_id })
scrollToPosition('bottom', 50)
@scrollToPosition('bottom', 50, article_id)
return
# scroll to article if given
if article_id && article_id isnt @last_article_id
@last_article_id = article_id
scrollToPosition('article', 300)
@scrollToPosition('article', 300, article_id)
return
# scroll to end if new article has been added
if !@last_ticket_article_ids || !_.isEqual(_.sortBy(@last_ticket_article_ids), _.sortBy(@ticket_article_ids))
@last_ticket_article_ids = @ticket_article_ids
scrollToPosition('bottom', 100)
@scrollToPosition('bottom', 100, article_id)
return
setPosition: (position) =>
@@ -915,6 +915,7 @@ class App.TicketZoom extends App.Controller
@autosaveStart()
@muteTask()
@submitEnable(e)
@scrollToPosition('bottom', 50)
error: (settings, details) =>
error = undefined

View File

@@ -180,7 +180,11 @@ class EmailReply extends App.Controller
day: 'numeric'
year: 'numeric'
}
new Date(date_string).toLocaleTimeString('en-US', options)
locale = App.i18n.get() || 'en-US'
try
new Date(date_string).toLocaleTimeString(locale, options)
catch e
new Date(date_string).toLocaleTimeString('en-US', options)
@emailForward: (ticket, article, ui) ->

View File

@@ -1,6 +1,6 @@
<div class="alert alert--danger hidden" role="alert"></div>
<p>
<%- @T('The tutorial on how to manage a %s is hosted on our [online documentation](https://docs.zammad.org/en/latest/channel-telegram.html).', 'Telegram Bot') %>
<%- @T('The tutorial on how to manage a %s is hosted on our [online documentation](https://zammad-admin-documentation.readthedocs.io/en/latest/channels-telegram.html).', 'Telegram Bot') %>
</p>
<fieldset>
<h2><%- @T('Enter your %s App Keys', 'Telegram') %></h2>

View File

@@ -10,7 +10,7 @@
</div>
<% if @external_credential && @external_credential.credentials && !@external_credential.credentials.webhook_id: %>
<div class="alert alert--warning" role="alert"><%- @T('Your Twitter-App is not using the Twitter Account Activity API yet and is therefore limited to search terms only. Please refer to the documentation %l on how to update your account.', 'https://docs.zammad.org/en/latest/channel-twitter.html') %></div>
<div class="alert alert--warning" role="alert"><%- @T('Your Twitter-App is not using the Twitter Account Activity API yet and is therefore limited to search terms only. Please refer to the documentation %l on how to update your account.', 'https://zammad-admin-documentation.readthedocs.io/en/latest/channels-twitter.html') %></div>
<% end %>
<div class="page-content">

View File

@@ -314,10 +314,7 @@ curl http://localhost/api/v1/organization/{id} -v -u #{login}:#{password} -H "Co
organization = Organization.find(params[:id])
# get history of organization
history = organization.history_get(true)
# return result
render json: history
render json: organization.history_get(true)
end
# @path [GET] /organizations/import_example

View File

@@ -219,6 +219,7 @@ class ReportsController < ApplicationController
# needs to be skipped
objects = ObjectManager::Attribute.where(editable: true,
active: true,
to_create: false,
object_lookup_id: ObjectLookup.lookup(name: 'Ticket').id)
.pluck(:name, :display, :data_type, :data_option)
.map { |name, display, data_type, data_option| { name: name, display: display, data_type: data_type, data_option: data_option } }

View File

@@ -2,7 +2,7 @@
class SessionsController < ApplicationController
prepend_before_action :authentication_check, only: %i[switch_to_user list delete]
skip_before_action :verify_csrf_token, only: %i[create show destroy create_omniauth failure_omniauth create_sso]
skip_before_action :verify_csrf_token, only: %i[show destroy create_omniauth failure_omniauth create_sso]
# "Create" a login, aka "log the user in"
def create

View File

@@ -294,10 +294,7 @@ class TicketsController < ApplicationController
access!(ticket, 'read')
# get history of ticket
history = ticket.history_get(true)
# return result
render json: history
render json: ticket.history_get(true)
end
# GET /api/v1/ticket_related/1

View File

@@ -165,6 +165,7 @@ class TimeAccountingsController < ApplicationController
]
objects = ObjectManager::Attribute.where(editable: true,
active: true,
to_create: false,
object_lookup_id: ObjectLookup.lookup(name: 'Ticket').id)
.pluck(:name, :display, :data_type, :data_option)
.map { |name, display, data_type, data_option| { name: name, display: display, data_type: data_type, data_option: data_option } }

View File

@@ -502,10 +502,7 @@ class UsersController < ApplicationController
user = User.find(params[:id])
# get history of user
history = user.history_get(true)
# return result
render json: history
render json: user.history_get(true)
end
=begin

View File

@@ -49,6 +49,9 @@ add a new activity entry for an object
permission_id = permission.id
end
# check if object for online notification exists
exists_by_object_and_id?(data[:object], data[:o_id])
# check newest entry - is needed
result = ActivityStream.where(
o_id: data[:o_id],

View File

@@ -18,6 +18,7 @@ class ApplicationModel < ActiveRecord::Base
include ApplicationModel::ChecksImport
include ApplicationModel::CanTouchReferences
include ApplicationModel::CanQueryCaseInsensitiveWhereOrSql
include ApplicationModel::HasExistsCheckByObjectAndId
self.abstract_class = true
end

View File

@@ -145,7 +145,9 @@ get assets of object list
def assets_of_object_list(list, assets = {})
list.each do |item|
require_dependency item['object'].to_filename
record = Kernel.const_get(item['object']).find(item['o_id'])
record = Kernel.const_get(item['object']).lookup(id: item['o_id'])
next if record.blank?
assets = record.assets(assets)
if item['created_by_id'].present?
user = User.find(item['created_by_id'])

View File

@@ -134,7 +134,7 @@ returns
next if association_attributes_ignored.include?(assoc_name)
eager_load.push(assoc_name)
pluck.push("#{assoc.table_name}.id AS #{assoc_name}")
pluck.push("#{ActiveRecord::Base.connection.quote_table_name(assoc.table_name)}.id AS #{ActiveRecord::Base.connection.quote_table_name(assoc_name)}")
keys.push("#{assoc_name.to_s.singularize}_ids")
end

View File

@@ -0,0 +1,35 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::HasExistsCheckByObjectAndId
extend ActiveSupport::Concern
class_methods do
=begin
verify if referenced object exists
success = Model.exists_by_object_and_id('Ticket', 123)
returns
# true or will raise an exception
=end
def exists_by_object_and_id?(object, o_id)
begin
local_class = object.constantize
rescue => e
raise "Unable for get an instance of '#{object}': #{e.inspect}"
end
if !local_class.exists?(o_id)
raise ActiveRecord::RecordNotFound, "Unable for find reference object '#{object}.exists?(#{o_id})!"
end
true
end
end
end

View File

@@ -177,10 +177,12 @@ returns
next if !track_retweets? && tweet.retweet?
# ignore older messages
if (@channel.created_at - 15.days) > tweet.created_at.dup.utc || older_import >= older_import_max
older_import += 1
Rails.logger.debug { "tweet to old: #{tweet.id}/#{tweet.created_at}" }
next
if @sync[:import_older_tweets] != true
if (@channel.created_at - 15.days) > tweet.created_at.dup.utc || older_import >= older_import_max
older_import += 1
Rails.logger.debug { "tweet to old: #{tweet.id}/#{tweet.created_at}" }
next
end
end
next if @client.locale_sender?(tweet) && own_tweet_already_imported?(tweet)

View File

@@ -5,7 +5,7 @@
class Channel::EmailParser
EMAIL_REGEX = /.+@.+/
RECIPIENT_FIELDS = %w[to cc delivered-to x-original-to envelope-to].freeze
SENDER_FIELDS = %w[from reply-to return-path].freeze
SENDER_FIELDS = %w[from reply-to return-path sender].freeze
=begin

View File

@@ -38,7 +38,7 @@ module HasGroups
# @return [ActiveRecord::AssociationRelation<[<Group]>] List of Groups with :through attributes
def access(*access)
table_name = proxy_association.owner.class.group_through.table_name
query = select("groups.*, #{table_name}.*")
query = select("#{ActiveRecord::Base.connection.quote_table_name('groups')}.*, #{ActiveRecord::Base.connection.quote_table_name(table_name)}.*")
return query if access.blank?
access.push('full') if !access.include?('full')

View File

@@ -204,12 +204,14 @@ returns
=end
def history_get(fulldata = false)
relation_object = self.class.instance_variable_get(:@history_relation_object) || nil
if !fulldata
return History.list(self.class.name, self['id'])
return History.list(self.class.name, self['id'], relation_object)
end
# get related objects
history = History.list(self.class.name, self['id'], nil, true)
history = History.list(self.class.name, self['id'], relation_object, true)
history[:list].each do |item|
record = Kernel.const_get(item['object']).find(item['o_id'])
@@ -242,5 +244,21 @@ end
def history_attributes_ignored(*attributes)
@history_attributes_ignored = attributes
end
=begin
serve methode to ignore model attributes in historization
class Model < ApplicationModel
include HasHistory
history_relation_object 'Some::Relation::Object'
end
=end
def history_relation_object(attribute)
@history_relation_object = attribute
end
end
end

View File

@@ -40,19 +40,19 @@ add a new history entry for an object
return if Setting.get('import_mode') && !data[:id]
# lookups
if data[:history_type]
if data[:history_type].present?
history_type = type_lookup(data[:history_type])
end
if data[:history_object]
if data[:history_object].present?
history_object = object_lookup(data[:history_object])
end
related_history_object_id = nil
if data[:related_history_object]
if data[:related_history_object].present?
related_history_object = object_lookup(data[:related_history_object])
related_history_object_id = related_history_object.id
end
history_attribute_id = nil
if data[:history_attribute]
if data[:history_attribute].present?
history_attribute = attribute_lookup(data[:history_attribute])
history_attribute_id = history_attribute.id
end
@@ -80,11 +80,11 @@ add a new history entry for an object
if history_record
history_record.update!(record)
else
record_new = History.create(record)
record_new = History.create!(record)
if record[:id]
record_new.id = record[:id]
end
record_new.save
record_new.save!
end
end
@@ -148,7 +148,7 @@ returns
=end
def self.list(requested_object, requested_object_id, related_history_object = nil, assets = nil)
if !related_history_object
if related_history_object.blank?
history_object = object_lookup(requested_object)
history = History.where(history_object_id: history_object.id)
.where(o_id: requested_object_id)
@@ -220,14 +220,10 @@ returns
def self.type_lookup(name)
# lookup
history_type = History::Type.lookup(name: name)
if history_type
return history_type
end
return history_type if history_type
# create
History::Type.create(
name: name
)
History::Type.create!(name: name)
end
def self.object_lookup_id(id)
@@ -237,14 +233,10 @@ returns
def self.object_lookup(name)
# lookup
history_object = History::Object.lookup(name: name)
if history_object
return history_object
end
return history_object if history_object
# create
History::Object.create(
name: name
)
History::Object.create!(name: name)
end
def self.attribute_lookup_id(id)
@@ -254,14 +246,10 @@ returns
def self.attribute_lookup(name)
# lookup
history_attribute = History::Attribute.lookup(name: name)
if history_attribute
return history_attribute
end
return history_attribute if history_attribute
# create
History::Attribute.create(
name: name
)
History::Attribute.create!(name: name)
end
class Object < ApplicationModel

View File

@@ -41,6 +41,9 @@ add a new online notification for this user
object_id = ObjectLookup.by_name(data[:object])
end
# check if object for online notification exists
exists_by_object_and_id?(data[:object], data[:o_id])
record = {
o_id: data[:o_id],
object_lookup_id: object_id,

View File

@@ -15,9 +15,11 @@ class RecentView < ApplicationModel
def self.log(object, o_id, user)
return if !access(object, o_id, user)
RecentView.create(o_id: o_id,
recent_view_object_id: ObjectLookup.by_name(object),
created_by_id: user.id)
exists_by_object_and_id?(object, o_id)
RecentView.create!(o_id: o_id,
recent_view_object_id: ObjectLookup.by_name(object),
created_by_id: user.id)
end
def self.log_destroy(requested_object, requested_object_id)

View File

@@ -53,6 +53,8 @@ class Ticket < ApplicationModel
:article_count,
:preferences
history_relation_object 'Ticket::Article'
belongs_to :group
belongs_to :organization
has_many :articles, class_name: 'Ticket::Article', after_add: :cache_update, after_remove: :cache_update, dependent: :destroy, inverse_of: :ticket
@@ -888,7 +890,7 @@ perform changes on ticket
next if value['value'].blank?
next if value['value'] != 'delete'
logger.debug { "Deleted ticket from #{perform_origin} #{perform.inspect} Ticket.find(#{id})" }
logger.info { "Deleted ticket from #{perform_origin} #{perform.inspect} Ticket.find(#{id})" }
destroy!
next
end
@@ -1168,27 +1170,6 @@ result
Ticket::Article.where(ticket_id: id).order(:created_at, :id)
end
def history_get(fulldata = false)
list = History.list(self.class.name, self['id'], 'Ticket::Article')
return list if !fulldata
# get related objects
assets = {}
list.each do |item|
record = Kernel.const_get(item['object']).find(item['o_id'])
assets = record.assets(assets)
if item['related_object']
record = Kernel.const_get(item['related_object']).find( item['related_o_id'])
assets = record.assets(assets)
end
end
{
history: list,
assets: assets,
}
end
private
def check_generate

View File

@@ -248,7 +248,7 @@ function setup_elasticsearch () {
yes | /usr/share/elasticsearch/bin/elasticsearch-plugin -s install ingest-attachment
if [ "${ES_CONNECTION}" == "http://127.0.0.1:9200" ]; then
if [ "${ES_CONNECTION}" == "http://127.0.0.1:9200" ] || [ "${ES_CONNECTION}" == "http://localhost:9200" ]; then
${INIT_CMD} restart elasticsearch
else
echo -e "\n It seems you're running an external Elasticsearch server on ${ES_CONNECTION}"

View File

@@ -1,11 +0,0 @@
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: mysql2
database: zammad_test
pool: 50
timeout: 5000
encoding: utf8
username: some_user
password: some_pass

View File

@@ -1,11 +0,0 @@
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: postgresql
database: zammad_test
pool: 50
timeout: 5000
encoding: utf8
username: postgres
password:

View File

@@ -1,18 +0,0 @@
#!/bin/bash
#
# trigger build of https://github.com/zammad/zammad-docker on https://travis-ci.org/zammad/zammad-docker and upload it to https://hub.docker.com/r/zammad/zammad
#
REPO_USER="zammad"
REPO="zammad-docker"
BRANCH="master"
if [ "${TRAVIS_BRANCH}" == 'develop' ]; then
curl -X POST \
-H "Content-Type: application/json" \
-H "Travis-API-Version: 3" \
-H "Accept: application/json" \
-H "Authorization: token ${TRAVIS_API_TOKEN}" \
-d '{"request":{ "message": "'"${TRAVIS_COMMIT_MESSAGE}"'","branch":"'${BRANCH}'","config":{"env":{"ZAMMAD_VERSION":"'${ZAMMAD_VERSION}'"}}}}' \
"https://api.travis-ci.org/repo/${REPO_USER}%2F${REPO}/requests"
fi

View File

@@ -1,18 +0,0 @@
#!/bin/bash
#
# trigger build of https://github.com/zammad/zammad-docker-compose on https://travis-ci.org/zammad/zammad-docker-compose and upload it to https://hub.docker.com/r/zammad/zammad-docker-compose/
#
REPO_USER="zammad"
REPO="zammad-docker-compose"
BRANCH="master"
if [ "${TRAVIS_BRANCH}" == 'stable' ]; then
curl -X POST \
-H "Content-Type: application/json" \
-H "Travis-API-Version: 3" \
-H "Accept: application/json" \
-H "Authorization: token ${TRAVIS_API_TOKEN}" \
-d '{"request":{ "message": "'"${TRAVIS_COMMIT_MESSAGE}"'","branch":"'${BRANCH}'","config":{"env":{"ZAMMAD_VERSION":"'${ZAMMAD_VERSION}'"}}}}' \
"https://api.travis-ci.org/repo/${REPO_USER}%2F${REPO}/requests"
fi

View File

@@ -1,18 +0,0 @@
#!/bin/bash
#
# trigger build of https://github.com/zammad/zammad-docker-univention on https://travis-ci.org/zammad/zammad-docker-univention and upload it to https://hub.docker.com/r/zammad/zammad-docker-univention
#
REPO_USER="zammad"
REPO="zammad-docker-univention"
BRANCH="master"
if [ "${TRAVIS_BRANCH}" == 'stable' ]; then
curl -X POST \
-H "Content-Type: application/json" \
-H "Travis-API-Version: 3" \
-H "Accept: application/json" \
-H "Authorization: token ${TRAVIS_API_TOKEN}" \
-d '{"request":{ "message": "'"${TRAVIS_COMMIT_MESSAGE}"'","branch":"'${BRANCH}'","config":{"env":{"ZAMMAD_VERSION":"'${ZAMMAD_VERSION}'"}}}}' \
"https://api.travis-ci.org/repo/${REPO_USER}%2F${REPO}/requests"
fi

View File

@@ -109,6 +109,10 @@ or with filter:
method: method,
params: params,
version: '2.0',
# the id attribute is required by the JSON-RPC standard
# but i-doit doesn't actually use it so we send a hard coded id
# see issue #2412 and community topic for further information
id: 42,
},
{
json: true,

View File

@@ -210,7 +210,7 @@ retunes
=end
def self.already_sent?(ticket, recipient, type)
result = ticket.history_get()
result = ticket.history_get
count = 0
result.each do |item|
next if item['type'] != 'notification'

View File

@@ -36,8 +36,8 @@ class Sequencer
resource.content_url,
{},
{
open_timeout: 10,
read_timeout: 60,
open_timeout: 20,
read_timeout: 240,
},
)
end

View File

@@ -11,7 +11,7 @@ namespace :zammad do
next if args[:elasticsearch] != 'with_elasticsearch'
Setting.set('es_url', 'http://localhost:9200')
Setting.set('es_url', 'http://elasticsearch:9200')
Setting.set('es_index', "browser_test_#{ENV['CI_BUILD_ID']}")
Rake::Task['searchindex:rebuild'].invoke

View File

@@ -348,9 +348,9 @@ test("check pretty date", function() {
if (diff < (60 * 60 * 24 * 6))
string = weekday + ' ' + date.getHours() + ':' + (date.getMinutes() < 10 ? '0':'') + date.getMinutes()
else if (current.getYear() == date.getYear())
string = weekday + ' ' + date.getDate() + '. ' + month + ' ' + (H < 10 ? '0':'') + H + ":" + (M < 10 ? '0':'') + M
string = weekday + ' ' + date.getDate() + '. ' + month + ' ' + H + ":" + (M < 10 ? '0':'') + M
else
string = weekday + ' ' + (m < 10 ? '0':'') + m + '/' + (d < 10 ? '0':'') + d + '/' + (yfull) + ' ' + (H < 10 ? '0':'') + H + ":" + (M < 10 ? '0':'') + M
string = weekday + ' ' + (m < 10 ? '0':'') + m + '/' + (d < 10 ? '0':'') + d + '/' + (yfull) + ' ' + H + ":" + (M < 10 ? '0':'') + M
return string;
}

View File

@@ -0,0 +1,38 @@
require 'yaml'
cnf = YAML.load_file(File.join(__dir__, '../../config/database/database.yml'))
cnf.delete('default')
hostsfile = '/etc/hosts'
database = %w[postgresql mysql].find do |possible_database|
File.foreach(hostsfile).any? { |l| l[possible_database] }
end
raise "Can't find any supported database in #{hostsfile}." if database.nil?
db_settings_map = {
'postgresql' => {
'adapter' => 'postgresql',
'username' => 'zammad',
'password' => 'zammad',
'host' => 'postgresql', # db alias from gitlab-ci.yml
},
'mysql' => {
'adapter' => 'mysql2',
'username' => 'root',
'password' => 'zammad',
'host' => 'mysql', # db alias from gitlab-ci.yml
}
}
# fetch DB settings from settings map and fallback to postgresql
db_settings = db_settings_map.fetch(database) { db_settings_map['postgresql'] }
%w[development test production].each do |environment|
cnf[environment].merge!(db_settings)
end
File.open('config/database.yml', 'w') do |file|
file.write(Psych.dump(cnf))
end

View File

@@ -1,4 +0,0 @@
#!/bin/bash
rake db:drop RAILS_ENV=test
rake db:drop RAILS_ENV=production

View File

@@ -1,74 +0,0 @@
#!/bin/bash
if [ "$RNAME" == '' ]; then
echo "ERROR: Env RNAME is missing!"
exit 1
fi
if [ "$RDB" == '' ]; then
echo "ERROR: Env RDB is missing!"
exit 1
fi
export RAND=`perl -e 'print int(rand(999999999))'`
DBNAME="$RNAME-$RAND"
DBFILE=config/database.yml
DB_PRODUCTION=$DBNAME"_production"
DB_TEST=$DBNAME"_test"
DB_DEVELOPMENT=$DBNAME"_development"
echo "Creating $DBFILE for $RDB tests with $DBNAME"
if [ "$RDB" == 'mysql' ]; then
echo "production:" > $DBFILE
echo " adapter: mysql2" >> $DBFILE
echo " database: $DB_PRODUCTION" >> $DBFILE
echo " pool: 50" >> $DBFILE
echo " timeout: 5000" >> $DBFILE
echo " encoding: utf8" >> $DBFILE
echo " username: root" >> $DBFILE
echo " password:" >> $DBFILE
echo "test:" >> $DBFILE
echo " adapter: mysql2" >> $DBFILE
echo " database: $DB_TEST" >> $DBFILE
echo " pool: 50" >> $DBFILE
echo " timeout: 5000" >> $DBFILE
echo " encoding: utf8" >> $DBFILE
echo " username: root" >> $DBFILE
echo " password:" >> $DBFILE
echo "development:" >> $DBFILE
echo " adapter: mysql2" >> $DBFILE
echo " database: $DB_DEVELOPMENT" >> $DBFILE
echo " pool: 50" >> $DBFILE
echo " timeout: 5000" >> $DBFILE
echo " encoding: utf8" >> $DBFILE
echo " username: root" >> $DBFILE
echo " password:" >> $DBFILE
fi
if [ "$RDB" == 'postgresql' ]; then
echo "production:" > $DBFILE
echo " adapter: postgresql" >> $DBFILE
echo " database: $DB_PRODUCTION" >> $DBFILE
echo " pool: 50" >> $DBFILE
echo " timeout: 5000" >> $DBFILE
echo " encoding: utf8" >> $DBFILE
echo " username: gitlab-runner" >> $DBFILE
echo " password:" >> $DBFILE
echo "test:" >> $DBFILE
echo " adapter: postgresql" >> $DBFILE
echo " database: $DB_TEST" >> $DBFILE
echo " pool: 50" >> $DBFILE
echo " timeout: 5000" >> $DBFILE
echo " encoding: utf8" >> $DBFILE
echo " username: gitlab-runner" >> $DBFILE
echo " password:" >> $DBFILE
echo "development:" >> $DBFILE
echo " adapter: postgresql" >> $DBFILE
echo " database: $DB_DEVELOPMENT" >> $DBFILE
echo " pool: 50" >> $DBFILE
echo " timeout: 5000" >> $DBFILE
echo " encoding: utf8" >> $DBFILE
echo " username: gitlab-runner" >> $DBFILE
echo " password:" >> $DBFILE
fi

View File

@@ -1,36 +0,0 @@
#!/bin/bash
RAILS_ENV=$1
APP_PORT=$2
WS_PORT=$3
EXIT=$4 || 0
WITH_DB=$5 || 0
WITH_ELASTICSEARCH=$6 || 0
SERVER_PID='tmp/pids/server.pid'
LOG_HOST='cilog@schneeberg.znuny.com'
bundle exec script/scheduler.rb stop
bundle exec script/websocket-server.rb stop
kill $(cat $SERVER_PID)
sleep 5
if [ -f $SERVER_PID ]; then
kill -9 $(cat $SERVER_PID)
fi
if test $WITH_DB -eq 1; then
script/build/test_cleanup.sh
fi
if test $WITH_ELASTICSEARCH -eq 1; then
rake searchindex:drop
fi
# if build has failed, copy logs for analyzing
if test $EXIT -eq 1; then
ssh $LOG_HOST "mkdir -p logs/$CI_BUILD_ID"
scp -C log/* $LOG_HOST:logs/$CI_BUILD_ID/
scp -C tmp/screenshot* $LOG_HOST:logs/$CI_BUILD_ID/
fi
exit $EXIT

View File

@@ -1,33 +0,0 @@
#!/bin/bash
RAILS_ENV=$1
APP_PORT=$2
WS_PORT=$3
WITH_DB=$4 || 0
WITH_ELASTICSEARCH=$5 || 0
if test $WITH_DB -eq 1; then
script/bootstrap.sh
fi
export ZAMMAD_SETTING_TTL=15
rails r "Setting.set('developer_mode', true)"
rails r "Setting.set('websocket_port', '$WS_PORT')"
rails r "Setting.set('fqdn', '$IP:$BROWSER_PORT')"
rails r "Setting.set('chat_agent_idle_timeout', '45')"
if test $WITH_ELASTICSEARCH -eq 1; then
rails r "Setting.set('es_url', 'http://localhost:9200')"
rails r "Setting.set('es_index', 'browser_test_$CI_BUILD_ID')"
rake searchindex:rebuild
fi
echo "env used for script/build/test_startup.sh $1 $2 $3"
echo "export RAILS_ENV=$RAILS_ENV"
echo "export IP=$IP"
echo "export BROWSER_PORT=$BROWSER_PORT"
#rails s puma -d --pid tmp/pids/server.pid --bind 0.0.0.0 --port $APP_PORT
bundle exec puma --pidfile tmp/pids/server.pid -d -p $APP_PORT -e $RAILS_ENV
bundle exec script/websocket-server.rb start -d -p $WS_PORT
bundle exec script/scheduler.rb start

View File

@@ -0,0 +1,32 @@
require 'rails_helper'
RSpec.describe Sequencer::Unit::Import::Zendesk::Ticket::Comment::Attachment::Request, sequencer: :unit do
context 'when fetching large attachements from Zendesk' do
before(:all) do
described_class.class_eval do
private
def failed?
false
end
end
end
def mock_parameters
{
resource: double(
content_url: ''
)
}
end
it 'open timeout should be 20s and read timeout should be 240s' do
expect(UserAgent).to receive(:get).with(any_args, { open_timeout: 20, read_timeout: 240 })
process(mock_parameters)
end
end
end

View File

@@ -21,6 +21,7 @@ RSpec.describe ::Channel::Driver::Twitter do
id: 'system_id',
},
sync: {
import_older_tweets: true,
track_retweets: true,
search: [
{

View File

@@ -2,6 +2,17 @@ require 'rails_helper'
RSpec.describe 'Api Auth', type: :request do
around(:each) do |example|
orig = ActionController::Base.allow_forgery_protection
begin
ActionController::Base.allow_forgery_protection = true
example.run
ensure
ActionController::Base.allow_forgery_protection = orig
end
end
let(:admin_user) do
create(:admin_user)
end
@@ -369,7 +380,10 @@ RSpec.describe 'Api Auth', type: :request do
it 'does session auth - admin' do
create(:admin_user, login: 'api-admin@example.com', password: 'adminpw')
post '/api/v1/signin', params: { username: 'api-admin@example.com', password: 'adminpw', fingerprint: '123456789' }
get '/'
token = response.headers['CSRF-TOKEN']
post '/api/v1/signin', params: { username: 'api-admin@example.com', password: 'adminpw', fingerprint: '123456789' }, headers: { 'X-CSRF-Token' => token }
expect(response.header.key?('Access-Control-Allow-Origin')).to be_falsey
expect(response).to have_http_status(201)

View File

@@ -452,6 +452,22 @@ RSpec.describe 'Organization', type: :request, searchindex: true do
end
it 'does organization history' do
organization1 = create(
:organization,
name: 'some org',
)
authenticated_as(agent_user)
get "/api/v1/organizations/history/#{organization1.id}", params: {}, as: :json
expect(response).to have_http_status(200)
expect(json_response).to be_a_kind_of(Hash)
expect(json_response['history'].class).to eq(Array)
expect(json_response['assets'].class).to eq(Hash)
expect(json_response['assets']['Ticket']).to be_nil
expect(json_response['assets']['Organization'][organization1.id.to_s]).not_to be_nil
end
it 'does csv example - customer no access' do
authenticated_as(customer_user)
get '/api/v1/organizations/import_example', params: {}, as: :json

View File

@@ -2078,6 +2078,30 @@ RSpec.describe 'Ticket', type: :request do
expect(json_response).to be_a_kind_of(Hash)
expect(json_response['tickets']).to eq([ticket2.id, ticket1.id])
end
it 'does ticket history ' do
ticket1 = create(
:ticket,
title: 'some title',
group: ticket_group,
customer_id: customer_user.id,
)
create(
:ticket_article,
type: Ticket::Article::Type.lookup(name: 'note'),
sender: Ticket::Article::Sender.lookup(name: 'Customer'),
ticket_id: ticket1.id,
)
authenticated_as(agent_user)
get "/api/v1/ticket_history/#{ticket1.id}", params: {}, as: :json
expect(response).to have_http_status(200)
expect(json_response).to be_a_kind_of(Hash)
expect(json_response['history'].class).to eq(Array)
expect(json_response['assets'].class).to eq(Hash)
expect(json_response['assets']['User'][customer_user.id.to_s]).not_to be_nil
expect(json_response['assets']['Ticket'][ticket1.id.to_s]).not_to be_nil
end
end
describe 'stats' do

View File

@@ -39,5 +39,27 @@ RSpec.describe 'Time Accounting API endpoints', type: :request do
expect(response['Content-Type']).to eq('application/vnd.ms-excel')
end
end
# Regression test for issue #2398 - Missing custom object in database causes error on export in time_accounting
# This test is identical to the above one, except with the added step of a pending migration in the beginning
context 'with pending attribute migrations, requesting a log report download' do
it 'responds with an Excel spreadsheet' do
ObjectManager::Attribute.add attributes_for :object_manager_attribute_select
group = create(:group)
ticket = create(:ticket, state: Ticket::State.lookup(name: 'open'), customer: customer )
article = create(:ticket_article, ticket: ticket, type: Ticket::Article::Type.lookup(name: 'note') )
create(:ticket_time_accounting, ticket_id: ticket.id, ticket_article_id: article.id)
authenticated_as(admin)
get "/api/v1/time_accounting/log/by_ticket/#{year}/#{month}?download=true", params: {}
expect(response).to have_http_status(200)
expect(response['Content-Disposition']).to be_truthy
expect(response['Content-Disposition']).to eq("attachment; filename=\"by_ticket-#{year}-#{month}.xls\"")
expect(response['Content-Type']).to eq('application/vnd.ms-excel')
end
end
end
end

View File

@@ -919,6 +919,25 @@ RSpec.describe 'User', type: :request, searchindex: true do
user2.destroy!
end
it 'does user history' do
user1 = create(
:customer_user,
login: 'history@example.com',
firstname: 'History',
lastname: 'Customer1',
email: 'history@example.com',
)
authenticated_as(agent_user)
get "/api/v1/users/history/#{user1.id}", params: {}, as: :json
expect(response).to have_http_status(200)
expect(json_response).to be_a_kind_of(Hash)
expect(json_response['history'].class).to eq(Array)
expect(json_response['assets'].class).to eq(Hash)
expect(json_response['assets']['Ticket']).to be_nil
expect(json_response['assets']['User'][user1.id.to_s]).not_to be_nil
end
it 'does user search sortable' do
firstname = "user_search_sortable #{rand(999_999_999)}"

View File

@@ -6,7 +6,7 @@ VCR.configure do |config|
config.ignore_request do |request|
uri = URI(request.uri)
['zammad.com', 'google.com'].any? do |site|
['zammad.com', 'google.com', 'elasticsearch', 'selenium'].any? do |site|
uri.host.include?(site)
end
end

View File

@@ -98,13 +98,7 @@ class AdminChannelEmailTest < TestCase
value: 'Users',
)
click(css: '.modal button.js-submit')
sleep 2
watch_for_disappear(
css: '.modal',
)
sleep 2
exists_not(css: '.modal')
modal_disappear(timeout: 20)
watch_for(
css: '.content.active',

View File

@@ -11,7 +11,7 @@ class AdminPermissionsGranularVsFullTest < TestCase
)
tasks_close_all()
click(css: '.user-menu a[title=Admin')
click(css: 'a[href="#manage"]')
click(css: '.content.active a[href="#manage/groups"]')
click(css: '.content.active a[data-type="new"]')
@@ -21,26 +21,34 @@ class AdminPermissionsGranularVsFullTest < TestCase
element.clear
element.send_keys(new_group_name)
click(css: '.modal button.js-submit')
sleep(1)
modal_disappear
click(css: '.content.active a[href="#manage/users"]')
user_css = '.user-list .js-tableBody tr td'
watch_for(css: user_css)
click(css: user_css)
@browser.find_elements(css: '.content.active .user-list td:first-child').each do |entry|
next if entry.text.strip != 'master@example.com'
entry.click
break
end
modal_ready
scroll_script = "var el = document.getElementsByClassName('modal')[0];"
scroll_script += 'el.scrollTo(0, el.scrollHeight);'
@browser.execute_script scroll_script
group = @browser.find_elements(css: '.modal .settings-list tbody tr').find do |el|
el.find_element(css: 'td').text == new_group_name
end
if !group
screenshot(comment: 'group_not_found')
raise "Can't find group #{new_group_name}"
end
toggle_checkbox(group, 'full')
sleep(1)
assert(checkbox_is_selected(group, 'full'))

View File

@@ -129,4 +129,76 @@ class AgentTicketEmailReplyKeepBodyTest < TestCase
assert match[1]
assert Time.zone.parse(match[1])
end
# Regression test for issue #2344 - Missing translation for Full-Quote-Text "on xy wrote"
def test_full_quote_german_locale
@browser = instance = browser_instance
login(
username: 'master@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all()
ticket_open_by_title(
title: 'Welcome to Zammad',
)
watch_for(
css: '.content.active .js-settingContainer .js-setting .dropdown-icon',
)
# enable email full quote in the ticket zoom config page
scroll_to(
position: 'botton',
css: '.content.active .js-settingContainer .js-setting .dropdown-icon',
)
click(css: '.content.active .js-settingContainer .js-setting .dropdown-icon')
modal_ready()
select(
css: '.modal #ui_ticket_zoom_article_email_full_quote select[name="ui_ticket_zoom_article_email_full_quote"]',
value: 'yes'
)
click(
css: '.modal #ui_ticket_zoom_article_email_full_quote .btn[type="submit"]',
)
modal_close()
modal_disappear()
exists(css: '.content.active .ticket-article [data-type="emailReply"]')
# switch user profile language to German
switch_language(
data: {
language: 'Deutsch'
},
)
ticket_open_by_title(
title: 'Welcome to Zammad',
)
scroll_to(
position: 'botton',
css: '.content.active .ticket-article [data-type="emailReply"]',
)
click(css: '.content.active .ticket-article [data-type="emailReply"]')
full_text = @browser.find_element(css: '.content.active .article-new .articleNewEdit-body').text
match = full_text.match(/\nAm (.*?), schrieb Nicole Braun:/)
assert match
datestamp = match[1]
assert datestamp
assert Time.zone.parse(datestamp)
day_of_week = datestamp.split(',').first
assert %w[Montag Dienstag Mittwoch Donnerstag Freitag Samstag Sonntag].include? day_of_week
# switch user profile language to English again for other tests
switch_language(
data: {
language: 'English (United States)'
},
)
end
end

View File

@@ -103,6 +103,9 @@ class AgentTicketTagTest < TestCase
browser: browser2,
number: ticket3[:number],
)
empty_search(
browser: browser2,
)
# set tag #1
click(
@@ -272,6 +275,9 @@ class AgentTicketTagTest < TestCase
browser: browser2,
number: ticket3[:number],
)
empty_search(
browser: browser2,
)
# verify tags
tags_verify(
@@ -353,7 +359,7 @@ class AgentTicketTagTest < TestCase
end
def test_b_tags
tag_prefix = "tag#{rand(999_999_999)}"
tag_prefix = "tag#{rand(1000)}"
@browser = browser_instance
login(

View File

@@ -63,6 +63,7 @@ class MaintenanceLoginMessageTest < TestCase
watch_for_disappear(
browser: browser2,
css: '.js-maintenanceLogin',
timeout: 30,
)
end

View File

@@ -46,7 +46,15 @@ class TestCase < Test::Unit::TestCase
end
def browser_url
ENV['BROWSER_URL'] || 'http://localhost:3000'
return ENV['BROWSER_URL'] if ENV['BROWSER_URL'].present?
"http://#{host}:3000"
end
def host
return 'localhost' if ENV['CI'].blank?
Socket.ip_address_list.detect(&:ipv4_private?).ip_address
end
def browser_instance
@@ -75,10 +83,10 @@ class TestCase < Test::Unit::TestCase
begin
local_browser = browser_instance_remote
break
rescue
rescue => e
wait_until_ready = rand(5..13)
log('browser_instance', { rescure: true, count: count, sleep: wait_until_ready, exception: e })
sleep wait_until_ready
log('browser_instance', { rescure: true, count: count, sleep: wait_until_ready })
end
end
@@ -93,10 +101,22 @@ class TestCase < Test::Unit::TestCase
if ENV['BROWSER_VERSION']
caps.version = ENV['BROWSER_VERSION']
end
# (ironically) required for timeout checks
# https://github.com/zalando/zalenium/issues/469#issuecomment-371417340
# https://opensource.zalando.com/zalenium/#usage
caps['idleTimeout'] = 300
http_client = Selenium::WebDriver::Remote::Http::Default.new(
open_timeout: 120,
read_timeout: 120
)
local_browser = Selenium::WebDriver.for(
:remote,
url: ENV['REMOTE_URL'],
desired_capabilities: caps,
http_client: http_client,
)
@browsers[local_browser.hash] = local_browser
browser_instance_preferences(local_browser)
@@ -2647,18 +2667,9 @@ wait untill text in selector disabppears
element.send_keys(params[:number])
sleep 3
empty_search(browser: instance)
# search by number again
element = instance.find_elements(css: '#global-search')[0]
element.click
element.clear
element.send_keys(params[:number])
sleep 1
# open ticket
#instance.find_element(partial_link_text: params[:number] } ).click
instance.execute_script("$(\".js-global-search-result a:contains('#{params[:number]}') .nav-tab-icon\").first().click()")
instance.execute_script("$(\".js-global-search-result a:contains('#{params[:number]}') .nav-tab-name\").first().click()")
watch_for(
browser: instance,
css: '.content.active .ticketZoom-header .ticket-number'
@@ -2695,7 +2706,7 @@ wait untill text in selector disabppears
# open ticket
#instance.find_element(partial_link_text: params[:title] } ).click
instance.execute_script("$(\".js-global-search-result a:contains('#{params[:title]}') .nav-tab-icon\").click()")
instance.execute_script("$(\".js-global-search-result a:contains('#{params[:title]}') .nav-tab-name\").first().click()")
sleep 1
title = instance.find_elements(css: '.content.active .ticketZoom-header .js-objectTitle')[0].text
if !title.match?(/#{params[:title]}/)
@@ -2782,7 +2793,7 @@ wait untill text in selector disabppears
element.send_keys(params[:value])
sleep 2
#instance.find_element(partial_link_text: params[:value] } ).click
instance.execute_script("$(\".js-global-search-result a:contains('#{params[:value]}') .nav-tab-icon\").click()")
instance.execute_script("$(\".js-global-search-result a:contains('#{params[:value]}') .nav-tab-name\").first().click()")
watch_for(
browser: instance,
css: '.content.active h1'
@@ -2818,7 +2829,7 @@ wait untill text in selector disabppears
sleep 3
#instance.find_element(partial_link_text: params[:value]).click
instance.execute_script("$(\".js-global-search-result a:contains('#{params[:value]}') .nav-tab-icon\").click()")
instance.execute_script("$(\".js-global-search-result a:contains('#{params[:value]}') .nav-tab-name\").first().click()")
watch_for(
browser: instance,
css: '.content.active h1'
@@ -4440,6 +4451,55 @@ wait untill text in selector disabppears
scope.find_element(css: "input[value=#{value}]").property('checked')
end
=begin
Switch the current logged in user's profile language to a new language
switch_language(
browser: browser2,
data: {
language: 'Deutsch'
},
)
IMPORTANT REMINDER! At the end of tests, the caller must manually set the language back to English again:
switch_language(
browser: browser2,
data: {
language: 'English (United States)'
},
)
Failure to switch back to English will cause large amounts of subsequent tests to fail due to the UI language differences.
=end
def switch_language(params = {})
switch_window_focus(params)
log('switch_language', params)
instance = params[:browser] || @browser
data = params[:data]
click(browser: instance, css: '#navigation .user-menu .js-avatar')
click(browser: instance, css: '#navigation .user-menu a[href="#profile"]')
select(
browser: instance,
css: '.content.active .searchableSelect-shadow',
value: data[:language],
)
click(browser: instance, css: '.content.active .btn--primary')
watch_for(
browser: instance,
css: '#notify',
)
end
=begin
Retrieve a hash of all the avaiable Zammad settings and their current values.

View File

@@ -75,7 +75,7 @@ class IdoitControllerTest < ActionDispatch::IntegrationTest
assert_equal('Not authorized (user)!', result['error'])
stub_request(:post, "#{@endpoint}src/jsonrpc.php")
.with(body: "{\"method\":\"cmdb.object_types\",\"params\":{\"apikey\":\"#{@token}\"},\"version\":\"2.0\"}")
.with(body: "{\"method\":\"cmdb.object_types\",\"params\":{\"apikey\":\"#{@token}\"},\"version\":\"2.0\",\"id\":42}")
.to_return(status: 200, body: read_messaage('object_types_response'), headers: {})
admin_credentials = ActionController::HttpAuthentication::Basic.encode_credentials('idoit-admin@example.com', 'adminpw')
@@ -114,7 +114,7 @@ class IdoitControllerTest < ActionDispatch::IntegrationTest
test 'list all object types' do
stub_request(:post, "#{@endpoint}src/jsonrpc.php")
.with(body: "{\"method\":\"cmdb.object_types\",\"params\":{\"apikey\":\"#{@token}\"},\"version\":\"2.0\"}")
.with(body: "{\"method\":\"cmdb.object_types\",\"params\":{\"apikey\":\"#{@token}\"},\"version\":\"2.0\",\"id\":42}")
.to_return(status: 200, body: read_messaage('object_types_response'), headers: {})
agent_credentials = ActionController::HttpAuthentication::Basic.encode_credentials('idoit-agent@example.com', 'agentpw')
@@ -156,7 +156,7 @@ class IdoitControllerTest < ActionDispatch::IntegrationTest
test 'query objects' do
stub_request(:post, "#{@endpoint}src/jsonrpc.php")
.with(body: "{\"method\":\"cmdb.objects\",\"params\":{\"apikey\":\"#{@token}\",\"filter\":{\"ids\":[\"33\"]}},\"version\":\"2.0\"}")
.with(body: "{\"method\":\"cmdb.objects\",\"params\":{\"apikey\":\"#{@token}\",\"filter\":{\"ids\":[\"33\"]}},\"version\":\"2.0\",\"id\":42}")
.to_return(status: 200, body: read_messaage('object_types_filter_response'), headers: {})
agent_credentials = ActionController::HttpAuthentication::Basic.encode_credentials('idoit-agent@example.com', 'agentpw')

View File

@@ -107,6 +107,45 @@ Some Textäöü",
],
},
},
{
data: "Sender: me_sender@example.com
To: customer@example.com
Subject: äöü some subject 3
Some Textäöü",
channel: {
trusted: false,
},
success: true,
result: {
0 => {
priority: '2 normal',
title: 'äöü some subject 3',
},
1 => {
body: 'Some Textäöü',
sender: 'Customer',
type: 'email',
internal: false,
},
},
verify: {
users: [
{
firstname: '',
lastname: '',
fullname: 'me@example.com',
email: 'me@example.com',
},
{
firstname: '',
lastname: '',
fullname: 'customer@example.com',
email: 'customer@example.com',
},
],
},
},
{
data: "From: me@example.com
To: customer@example.com

View File

@@ -1,9 +1,9 @@
require 'test_helper'
class HistoryTest < ActiveSupport::TestCase
current_user = User.lookup(email: 'nicole.braun@zammad.org')
test 'ticket' do
current_user = User.lookup(email: 'nicole.braun@zammad.org')
tests = [
# test 1
@@ -188,6 +188,7 @@ class HistoryTest < ActiveSupport::TestCase
end
test 'user' do
current_user = User.lookup(email: 'nicole.braun@zammad.org')
name = rand(999_999)
tests = [
@@ -275,6 +276,7 @@ class HistoryTest < ActiveSupport::TestCase
end
test 'organization' do
current_user = User.lookup(email: 'nicole.braun@zammad.org')
tests = [
# test 1
@@ -337,6 +339,57 @@ class HistoryTest < ActiveSupport::TestCase
organizations.each(&:destroy!)
end
test 'ticket assets' do
UserInfo.current_user_id = 1
agent1 = User.create!(
login: 'agent1@example.com',
firstname: 'agent',
lastname: '1',
email: 'agent1@example.com',
password: 'agentpw',
active: true,
roles: Role.where(name: %w[Agent Admin]),
groups: Group.all,
)
current_user = User.lookup(email: 'nicole.braun@zammad.org')
UserInfo.current_user_id = current_user.id
ticket = Ticket.create!(
title: 'test 1',
group: Group.first,
customer_id: current_user.id,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
)
article = Ticket::Article.create!(
ticket_id: ticket.id,
from: 'some_customer_com-1@example.com',
to: 'some_zammad_com-1@example.com',
subject: 'com test 1',
message_id: 'some@id_com_1',
body: 'some message 123',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
)
# verify if user of history record is in assets
UserInfo.current_user_id = agent1.id
ticket.state = Ticket::State.find_by(name: 'closed')
ticket.save!
# update updated_by (to not include agent1 in assets by ticket)
UserInfo.current_user_id = current_user.id
ticket.priority = Ticket::Priority.find_by(name: '3 high')
ticket.save!
history = ticket.history_get(true)
assert(history[:assets][:User][current_user.id])
assert(history[:assets][:User][agent1.id])
assert(history[:assets][:Ticket][ticket.id])
assert(history[:assets][:TicketArticle][article.id])
end
def history_check(history_list, history_check)
history_check.each do |check_item|
match = false

View File

@@ -8,32 +8,32 @@ class NotificationFactoryMailerTemplateTest < ActiveSupport::TestCase
groups = Group.where(name: 'Users')
roles = Role.where(name: 'Agent')
agent1 = User.create_or_update(
login: 'notification-template-agent1@example.com',
firstname: 'Notification<b>xxx</b>',
lastname: 'Agent1<b>yyy</b>',
email: 'notification-template-agent1@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
preferences: {
agent1 = User.create!(
login: 'notification-template-agent1@example.com',
firstname: 'Notification<b>xxx</b>',
lastname: 'Agent1<b>yyy</b>',
email: 'notification-template-agent1@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
preferences: {
locale: 'de-de',
},
updated_by_id: 1,
created_by_id: 1,
)
agent_current_user = User.create_or_update(
login: 'notification-template-current_user@example.com',
firstname: 'Notification Current',
lastname: 'User',
email: 'notification-template-current_user@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
preferences: {
agent_current_user = User.create!(
login: 'notification-template-current_user@example.com',
firstname: 'Notification Current',
lastname: 'User',
email: 'notification-template-current_user@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
preferences: {
locale: 'de-de',
},
updated_by_id: 1,

View File

@@ -83,34 +83,34 @@ class NotificationFactoryMailerTest < ActiveSupport::TestCase
groups = Group.all
roles = Role.where(name: 'Agent')
agent1 = User.create_or_update(
login: 'notification-settings-agent1@example.com',
firstname: 'Notification<b>xxx</b>',
lastname: 'Agent1',
email: 'notification-settings-agent1@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
agent1 = User.create!(
login: 'notification-settings-agent1@example.com',
firstname: 'Notification<b>xxx</b>',
lastname: 'Agent1',
email: 'notification-settings-agent1@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
updated_by_id: 1,
created_by_id: 1,
)
agent2 = User.create_or_update(
login: 'notification-settings-agent2@example.com',
firstname: 'Notification<b>xxx</b>',
lastname: 'Agent2',
email: 'notification-settings-agent2@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
agent2 = User.create!(
login: 'notification-settings-agent2@example.com',
firstname: 'Notification<b>xxx</b>',
lastname: 'Agent2',
email: 'notification-settings-agent2@example.com',
password: 'agentpw',
active: true,
roles: roles,
groups: groups,
updated_by_id: 1,
created_by_id: 1,
)
group_notification_setting = Group.create_or_update(
name: 'NotificationSetting',
group_notification_setting = Group.create!(
name: 'NotificationSetting',
updated_by_id: 1,
created_by_id: 1,
)

View File

@@ -538,10 +538,22 @@ class OnlineNotificationTest < ActiveSupport::TestCase
end
test 'cleanup check' do
ticket1 = Ticket.create(
group: @group,
customer_id: @customer_user.id,
owner_id: User.lookup(login: '-').id,
title: 'Unit Test 1 (äöüß)!',
state_id: Ticket::State.lookup(name: 'closed').id,
priority_id: Ticket::Priority.lookup(name: '2 normal').id,
updated_by_id: @agent_user1.id,
created_by_id: @agent_user1.id,
)
online_notification1 = OnlineNotification.add(
type: 'create',
object: 'Ticket',
o_id: 123,
o_id: ticket1.id,
seen: false,
user_id: @agent_user1.id,
created_by_id: 1,
@@ -552,7 +564,7 @@ class OnlineNotificationTest < ActiveSupport::TestCase
online_notification2 = OnlineNotification.add(
type: 'create',
object: 'Ticket',
o_id: 123,
o_id: ticket1.id,
seen: true,
user_id: @agent_user1.id,
created_by_id: 1,
@@ -563,7 +575,7 @@ class OnlineNotificationTest < ActiveSupport::TestCase
online_notification3 = OnlineNotification.add(
type: 'create',
object: 'Ticket',
o_id: 123,
o_id: ticket1.id,
seen: false,
user_id: @agent_user1.id,
created_by_id: 1,
@@ -574,7 +586,7 @@ class OnlineNotificationTest < ActiveSupport::TestCase
online_notification4 = OnlineNotification.add(
type: 'create',
object: 'Ticket',
o_id: 123,
o_id: ticket1.id,
seen: true,
user_id: @agent_user1.id,
created_by_id: @agent_user1.id,
@@ -585,7 +597,7 @@ class OnlineNotificationTest < ActiveSupport::TestCase
online_notification5 = OnlineNotification.add(
type: 'create',
object: 'Ticket',
o_id: 123,
o_id: ticket1.id,
seen: true,
user_id: @agent_user1.id,
created_by_id: @agent_user2.id,
@@ -596,7 +608,7 @@ class OnlineNotificationTest < ActiveSupport::TestCase
online_notification6 = OnlineNotification.add(
type: 'create',
object: 'Ticket',
o_id: 123,
o_id: ticket1.id,
seen: true,
user_id: @agent_user1.id,
created_by_id: @agent_user1.id,
@@ -607,7 +619,7 @@ class OnlineNotificationTest < ActiveSupport::TestCase
online_notification7 = OnlineNotification.add(
type: 'create',
object: 'Ticket',
o_id: 123,
o_id: ticket1.id,
seen: true,
user_id: @agent_user1.id,
created_by_id: @agent_user2.id,
@@ -628,4 +640,32 @@ class OnlineNotificationTest < ActiveSupport::TestCase
OnlineNotification.destroy_all
end
test 'not existing object ref' do
assert_raises(RuntimeError) do
OnlineNotification.add(
type: 'create',
object: 'TicketNotExisting',
o_id: 123,
seen: false,
user_id: @agent_user1.id,
created_by_id: 1,
updated_by_id: 1,
created_at: Time.zone.now - 10.months,
updated_at: Time.zone.now - 10.months,
)
end
assert_raises(ActiveRecord::RecordNotFound) do
OnlineNotification.add(
type: 'create',
object: 'Ticket',
o_id: 123,
seen: false,
user_id: @agent_user1.id,
created_by_id: 1,
updated_by_id: 1,
created_at: Time.zone.now - 10.months,
updated_at: Time.zone.now - 10.months,
)
end
end
end